escargot/src/parser/esprima_cpp/esprima.cpp
Hyukwoo Park bb7c12cfd5 Optimize parser to skip the scanning of the whole function (#475)
* parsing of function including parameter list and function body is skipped in scanner mode
* m_bodySrc in InterpretedCodeBlock is removed because it is no longer necessary
* some operator overloading in FreeableNode and DestructibleNode is fixed

Signed-off-by: HyukWoo Park <hyukwoo.park@samsung.com>
2019-10-23 19:40:26 +09:00

5335 lines
216 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 "Messages.h"
#include "interpreter/ByteCode.h"
#include "parser/ast/AST.h"
#include "parser/CodeBlock.h"
#include "parser/Lexer.h"
#include "parser/Script.h"
#include "parser/ASTBuilder.h"
#define ALLOC_TOKEN(tokenName) Scanner::ScannerResult* tokenName = ALLOCA(sizeof(Scanner::ScannerResult), Scanner::ScannerResult, ec);
#define ASTNode typename ASTBuilder::ASTNode
#define ASTStatementContainer typename ASTBuilder::ASTStatementContainer
#define ASTNodeVector typename ASTBuilder::ASTNodeVector
#define BEGIN_FUNCTION_SCANNING(name) \
SyntaxChecker newBuilder; \
auto oldSubCodeBlockIndex = ++this->subCodeBlockIndex; \
auto oldScopeContext = pushScopeContext(name);
#define END_FUNCTION_SCANNING() \
popScopeContext(oldScopeContext); \
this->subCodeBlockIndex = oldSubCodeBlockIndex;
using namespace Escargot::EscargotLexer;
namespace Escargot {
namespace esprima {
struct Context {
// Escargot::esprima::Context always allocated on the stack
MAKE_STACK_ALLOCATED();
bool allowIn : 1;
bool allowYield : 1;
bool allowLexicalDeclaration : 1;
bool allowSuperCall : 1;
bool allowSuperProperty : 1;
bool isAssignmentTarget : 1;
bool isBindingElement : 1;
bool inFunctionBody : 1;
bool inIteration : 1;
bool inSwitch : 1;
bool inArrowFunction : 1;
bool inWith : 1;
bool inCatchClause : 1;
bool inLoop : 1;
bool inParameterParsing : 1;
bool hasRestrictedWordInArrayOrObjectInitializer : 1;
bool strict : 1;
Scanner::SmallScannerResult firstCoverInitializedNameError;
std::vector<AtomicString> catchClauseSimplyDeclaredVariableNames;
std::vector<std::pair<AtomicString, bool>> labelSet; // <LabelString, continue accepted>
};
struct Marker {
size_t index;
size_t lineNumber;
size_t lineStart;
};
struct MetaNode {
size_t index;
size_t line;
size_t column;
};
/*
struct ArrowParameterPlaceHolderNode {
String* type;
NodeVector params;
};
*/
struct DeclarationOptions {
bool inFor;
KeywordKind kind;
};
class Parser {
public:
// Parser always allocated on the stack
MAKE_STACK_ALLOCATED();
::Escargot::Context* escargotContext;
Scanner* scanner;
Scanner scannerInstance;
ASTAllocator& allocator;
enum SourceType {
Script,
Module
};
SourceType sourceType;
Scanner::ScannerResult lookahead;
Script::ModuleData* moduleData;
Context contextInstance;
Context* context;
CodeBlock* codeBlock;
Marker baseMarker;
Marker startMarker;
Marker lastMarker;
ASTFunctionScopeContext* currentScopeContext;
ASTFunctionScopeContext* lastPoppedScopeContext;
AtomicString lastUsingName;
std::function<void(AtomicString, LexicalBlockIndex, KeywordKind, bool)> nameDeclaredCallback;
bool trackUsingNames;
bool hasLineTerminator;
bool isParsingSingleFunction;
size_t stackLimit;
size_t subCodeBlockIndex;
LexicalBlockIndex lexicalBlockIndex;
LexicalBlockIndex lexicalBlockCount;
NumeralLiteralVector numeralLiteralVector;
ASTFunctionScopeContext fakeContext;
struct ParseFormalParametersResult {
ParameterNodeVector params;
std::vector<AtomicString> paramSet;
Scanner::SmallScannerResult stricted;
Scanner::SmallScannerResult firstRestricted;
const char* message;
ParseFormalParametersResult()
: message(nullptr)
{
}
};
Parser(::Escargot::Context* escargotContext, StringView code, bool isModule, size_t stackRemain, ExtendedNodeLOC startLoc = ExtendedNodeLOC(0, 0, 0))
: scannerInstance(escargotContext, code, startLoc.line, startLoc.column)
, allocator(escargotContext->astAllocator())
, fakeContext(escargotContext->astAllocator())
{
ASSERT(escargotContext != nullptr);
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;
this->lexicalBlockIndex = 0;
this->lexicalBlockCount = 0;
this->subCodeBlockIndex = 0;
this->lastPoppedScopeContext = &fakeContext;
this->currentScopeContext = nullptr;
this->trackUsingNames = true;
this->isParsingSingleFunction = false;
this->codeBlock = nullptr;
this->scanner = &scannerInstance;
if (stackRemain >= STACK_LIMIT_FROM_BASE) {
stackRemain = STACK_LIMIT_FROM_BASE;
}
this->sourceType = isModule ? Module : Script;
this->moduleData = isModule ? new Script::ModuleData() : nullptr;
this->hasLineTerminator = false;
this->context = &contextInstance;
this->context->allowIn = true;
this->context->allowYield = true;
this->context->allowLexicalDeclaration = false;
this->context->allowSuperCall = false;
this->context->allowSuperProperty = false;
this->context->isAssignmentTarget = true;
this->context->isBindingElement = true;
this->context->inFunctionBody = false;
this->context->inIteration = false;
this->context->inSwitch = false;
this->context->inArrowFunction = false;
this->context->inWith = false;
this->context->inCatchClause = false;
this->context->inLoop = false;
this->context->inParameterParsing = false;
this->context->hasRestrictedWordInArrayOrObjectInitializer = false;
this->context->strict = this->sourceType == Module;
this->setMarkers(startLoc);
}
void setMarkers(ExtendedNodeLOC startLoc)
{
this->baseMarker.index = startLoc.index;
this->baseMarker.lineNumber = this->scanner->lineNumber;
this->baseMarker.lineStart = 0;
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->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;
Scanner::ScannerResult* next = &this->lookahead;
this->scanner->lex(next);
this->hasLineTerminator = false;
if (this->context->strict && next->type == Token::IdentifierToken && this->scanner->isStrictModeReservedWord(next->relatedSource(this->scanner->source))) {
next->type = Token::KeywordToken;
}
}
this->lastMarker.index = this->scanner->index;
this->lastMarker.lineNumber = this->scanner->lineNumber;
this->lastMarker.lineStart = this->scanner->lineStart;
}
void insertNumeralLiteral(Value v)
{
ASSERT(!v.isPointerValue());
if (VectorUtil::findInVector(numeralLiteralVector, v) == VectorUtil::invalidIndex) {
if (numeralLiteralVector.size() < KEEP_NUMERAL_LITERDATA_IN_REGISTERFILE_LIMIT) {
numeralLiteralVector.push_back(v);
}
}
}
ASTFunctionScopeContext* popScopeContext(ASTFunctionScopeContext* lastPushedScopeContext)
{
auto ret = this->currentScopeContext;
this->lastUsingName = AtomicString();
this->lastPoppedScopeContext = ret;
this->currentScopeContext = lastPushedScopeContext;
return ret;
}
void pushScopeContext(ASTFunctionScopeContext* ctx)
{
this->currentScopeContext = ctx;
this->lastUsingName = AtomicString();
}
ASTFunctionScopeContext* pushScopeContext(AtomicString functionName)
{
auto parentContext = this->currentScopeContext;
// initialize subCodeBlockIndex before parsing of an internal function
this->subCodeBlockIndex = 0;
if (this->isParsingSingleFunction) {
fakeContext = ASTFunctionScopeContext(this->allocator);
pushScopeContext(&fakeContext);
return parentContext;
}
pushScopeContext(new (this->allocator) ASTFunctionScopeContext(this->allocator, this->context->strict));
this->currentScopeContext->m_functionName = functionName;
this->currentScopeContext->m_inWith = this->context->inWith;
if (parentContext) {
parentContext->appendChild(this->currentScopeContext);
}
return parentContext;
}
ALWAYS_INLINE void insertUsingName(AtomicString name)
{
if (this->lastUsingName == name) {
return;
}
this->currentScopeContext->insertUsingName(name, this->lexicalBlockIndex);
this->lastUsingName = name;
}
void extractNamesFromFunctionParams(ParseFormalParametersResult& paramsResult)
{
if (this->isParsingSingleFunction) {
return;
}
ParameterNodeVector& params = paramsResult.params;
const std::vector<AtomicString>& paramNames = paramsResult.paramSet;
bool hasParameterOtherThanIdentifier = false;
#ifndef NDEBUG
bool shouldHaveEqualNumberOfParameterListAndParameterName = true;
#endif
this->currentScopeContext->m_parameters.resizeWithUninitializedValues(params.size());
for (size_t i = 0; i < params.size(); i++) {
switch (params[i]->type()) {
case Identifier: {
this->currentScopeContext->m_parameters[i] = params[i]->name();
break;
}
case AssignmentPattern: {
hasParameterOtherThanIdentifier = true;
AtomicString id = params[i]->assignmentPatternName();
#ifndef NDEBUG
if (id.string()->length() == 0) {
shouldHaveEqualNumberOfParameterListAndParameterName = false;
}
#endif
this->currentScopeContext->m_parameters[i] = id;
break;
}
case ArrayPattern:
case ObjectPattern: {
hasParameterOtherThanIdentifier = true;
#ifndef NDEBUG
shouldHaveEqualNumberOfParameterListAndParameterName = false;
#endif
this->currentScopeContext->m_parameters[i] = AtomicString();
break;
}
case RestElement: {
hasParameterOtherThanIdentifier = true;
#ifndef NDEBUG
shouldHaveEqualNumberOfParameterListAndParameterName = false;
#endif
this->currentScopeContext->m_parameters.erase(i);
break;
}
default: {
RELEASE_ASSERT_NOT_REACHED();
}
}
}
#ifndef NDEBUG
if (shouldHaveEqualNumberOfParameterListAndParameterName) {
ASSERT(params.size() == paramNames.size());
}
#endif
this->currentScopeContext->m_hasParameterOtherThanIdentifier = hasParameterOtherThanIdentifier;
for (size_t i = 0; i < paramNames.size(); i++) {
this->currentScopeContext->insertVarName(paramNames[i], 0, true);
}
// Check if any identifier names are duplicated.
if (hasParameterOtherThanIdentifier || this->context->inArrowFunction) {
if (paramNames.size() < 2) {
return;
}
for (size_t i = 0; i < paramNames.size() - 1; i++) {
// Note: using this inner for loop because std::find didn't work for some reason.
for (size_t j = i + 1; j < paramNames.size(); j++) {
if (paramNames[i] == paramNames[j]) {
this->throwError("duplicate argument names are not allowed in this context");
}
}
}
}
}
bool tryToSkipFunctionParsing()
{
// try to skip the function parsing during the parsing of function call only
if (!this->isParsingSingleFunction) {
return false;
}
InterpretedCodeBlock* currentTarget = this->codeBlock->asInterpretedCodeBlock();
size_t orgIndex = this->lookahead.start;
this->expect(LeftParenthesis);
InterpretedCodeBlock* childBlock = currentTarget->childBlockAt(this->subCodeBlockIndex);
StringView src = childBlock->src();
this->scanner->index = src.length() + orgIndex;
this->scanner->lineNumber = childBlock->sourceElementStart().line;
this->scanner->lineStart = childBlock->sourceElementStart().index - childBlock->sourceElementStart().column;
this->lookahead.lineNumber = this->scanner->lineNumber;
this->lookahead.lineStart = this->scanner->lineStart;
this->lookahead.type = Token::PunctuatorToken;
this->lookahead.valuePunctuatorKind = PunctuatorKind::RightBrace;
this->expect(RightBrace);
// increase subCodeBlockIndex because parsing of an internal function is skipped
this->subCodeBlockIndex++;
return true;
}
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()) {
UTF8StringData d1 = arg0->toUTF8StringData();
UTF8StringData d2 = arg1->toUTF8StringData();
auto temp = utf8StringToUTF16String(messageFormat, strlen(messageFormat));
msg = UTF16StringDataNonGCStd(temp.data(), temp.length());
UTF16StringDataNonGCStd from(u"%s");
UTF16StringDataNonGCStd arg0Data(arg0->toUTF16StringData().data());
UTF16StringDataNonGCStd arg1Data(arg1->toUTF16StringData().data());
size_t start_pos = msg.find(from, 0);
RELEASE_ASSERT(start_pos != SIZE_MAX);
msg.replace(start_pos, from.length(), arg0Data);
start_pos = msg.find(from, start_pos + arg0Data.length());
RELEASE_ASSERT(start_pos != SIZE_MAX);
msg.replace(start_pos, from.length(), arg1Data);
} else if (arg0->length()) {
UTF8StringData d1 = arg0->toUTF8StringData();
auto temp = utf8StringToUTF16String(messageFormat, strlen(messageFormat));
msg = UTF16StringDataNonGCStd(temp.data(), temp.length());
UTF16StringDataNonGCStd from(u"%s");
UTF16StringDataNonGCStd argData(arg0->toUTF16StringData().data());
size_t start_pos = msg.find(from, 0);
RELEASE_ASSERT(start_pos != SIZE_MAX);
msg.replace(start_pos, from.length(), argData);
} 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;
ErrorHandler::throwError(index, line, column, new UTF16String(msg.data(), msg.length()), 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();
}
}
const char* checkTokenIdentifier(const unsigned char type)
{
switch (type) {
case Token::EOFToken:
return Messages::UnexpectedEOS;
case Token::IdentifierToken:
return Messages::UnexpectedIdentifier;
case Token::NumericLiteralToken:
return Messages::UnexpectedNumber;
case Token::StringLiteralToken:
return Messages::UnexpectedString;
case Token::TemplateToken:
return Messages::UnexpectedTemplate;
default:
return Messages::UnexpectedToken;
}
}
// Throw an exception because of an unexpected token.
void throwUnexpectedToken(const Scanner::SmallScannerResult& token, const char* message = nullptr)
{
ASSERT(token);
const char* msg;
if (message) {
msg = message;
} else {
msg = Messages::UnexpectedToken;
}
String* value;
if (token.type != InvalidToken) {
if (!msg) {
msg = checkTokenIdentifier(token.type);
if (token.type == Token::KeywordToken) {
if (this->scanner->isFutureReservedWord(token.relatedSource(this->scanner->source))) {
msg = Messages::UnexpectedReserved;
} else if (this->context->strict && this->scanner->isStrictModeReservedWord(token.relatedSource(this->scanner->source))) {
msg = Messages::StrictReservedWord;
}
}
} else if (token.type == Token::EOFToken) {
msg = Messages::UnexpectedEOS;
}
value = (String*)new StringView(token.relatedSource(this->scanner->source));
} 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.type != InvalidToken) {
const size_t index = token.start;
const size_t line = token.lineNumber;
const size_t column = token.start - this->lastMarker.lineStart + 1;
ErrorHandler::throwError(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;
ErrorHandler::throwError(index, line, column, new UTF16String(msgData.data(), msgData.length()), ErrorObject::SyntaxError);
}
}
ALWAYS_INLINE void collectComments()
{
this->scanner->scanComments();
}
void nextToken(Scanner::ScannerResult* token = nullptr)
{
if (token) {
*token = this->lookahead;
}
size_t tokenLineNumber = this->lookahead.lineNumber;
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;
Scanner::ScannerResult* next = &this->lookahead;
this->scanner->lex(next);
this->hasLineTerminator = tokenLineNumber != next->lineNumber;
if (this->context->strict && next->type == Token::IdentifierToken && this->scanner->isStrictModeReservedWord(next->relatedSource(this->scanner->source))) {
next->type = Token::KeywordToken;
}
//this->lookahead = *next;
/*
if (this->config.tokens && next.type !== Token.EOF) {
this->tokens.push(this->convertToken(next));
}
*/
}
void nextRegexToken(Scanner::ScannerResult* token)
{
ASSERT(token != nullptr);
this->collectComments();
this->scanner->scanRegExp(token);
// Prime the next lookahead.
this->lookahead = *token;
this->nextToken();
}
MetaNode createNode()
{
MetaNode n;
n.index = this->startMarker.index + this->baseMarker.index;
n.line = this->startMarker.lineNumber;
n.column = this->startMarker.index - this->startMarker.lineStart;
return n;
}
MetaNode startNode(Scanner::ScannerResult* token)
{
ASSERT(token != nullptr);
MetaNode n;
n.index = token->start + this->baseMarker.index;
n.line = token->lineNumber;
n.column = token->start - token->lineStart;
return n;
}
MetaNode startNode(Scanner::SmallScannerResult* token)
{
ASSERT(token != nullptr);
MetaNode n;
n.index = token->start + this->baseMarker.index;
n.line = token->lineNumber;
n.column = token->start - token->lineStart;
return n;
}
// FIXME remove finalize methods
template <typename T>
ALWAYS_INLINE T* finalize(MetaNode meta, T* node)
{
node->m_loc = NodeLOC(meta.index);
return node;
}
ALWAYS_INLINE SyntaxNode finalize(MetaNode meta, SyntaxNode node)
{
return node;
}
// Expect the next token to match the specified punctuator.
// If not, an exception will be thrown.
void expect(PunctuatorKind value)
{
ALLOC_TOKEN(token);
this->nextToken(token);
if (token->type != Token::PunctuatorToken || token->valuePunctuatorKind != value) {
this->throwUnexpectedToken(*token);
}
}
void expectCommaSeparator()
{
this->expect(PunctuatorKind::Comma);
}
// Expect the next token to match the specified keyword.
// If not, an exception will be thrown.
void expectKeyword(KeywordKind keyword)
{
ALLOC_TOKEN(token);
this->nextToken(token);
if (token->type != Token::KeywordToken || token->valueKeywordKind != keyword) {
this->throwUnexpectedToken(*token);
}
}
// Return true if the next token matches the specified punctuator.
bool match(PunctuatorKind value)
{
return this->lookahead.type == Token::PunctuatorToken && this->lookahead.valuePunctuatorKind == 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(const char* keyword)
{
return this->lookahead.type == Token::IdentifierToken && this->lookahead.valueStringLiteral(this->scanner).equals(keyword);
}
// Return true if the next token is an assignment operator
bool matchAssign()
{
if (this->lookahead.type != Token::PunctuatorToken) {
return false;
}
PunctuatorKind op = this->lookahead.valuePunctuatorKind;
if (op >= Substitution && op < SubstitutionEnd) {
return true;
}
return false;
}
void consumeSemicolon()
{
if (this->match(PunctuatorKind::SemiColon)) {
this->nextToken();
} else if (!this->hasLineTerminator) {
if (this->lookahead.type != Token::EOFToken && !this->match(PunctuatorKind::RightBrace)) {
this->throwUnexpectedToken(this->lookahead);
}
this->lastMarker.index = this->startMarker.index;
this->lastMarker.lineNumber = this->startMarker.lineNumber;
this->lastMarker.lineStart = this->startMarker.lineStart;
}
}
void throwIfSuperOperationIsNotAllowed()
{
ALLOC_TOKEN(token);
this->nextToken(token);
if (this->match(LeftParenthesis)) {
if (!this->context->allowSuperCall) {
this->throwUnexpectedToken(*token);
}
} else if (this->match(Period) || this->match(LeftSquareBracket)) {
if (!this->context->allowSuperProperty) {
this->throwUnexpectedToken(*token);
}
} else {
this->throwUnexpectedToken(this->lookahead);
}
}
bool qualifiedPropertyName(Scanner::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->valuePunctuatorKind == LeftSquareBracket;
default:
return false;
}
}
bool isPropertyKey(Node* key, const StringView& name, 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;
}
bool isPropertyKey(SyntaxNode key, const StringView& name, const char* value)
{
if (key.type() == Identifier) {
return key.name() == value;
} else if (key.type() == Literal) {
return name.equals(value);
}
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 std::vector<Scanner::SmallScannerResult> SmallScannerResultVector;
struct IsolateCoverGrammarContext {
bool previousIsBindingElement;
bool previousIsAssignmentTarget;
Scanner::SmallScannerResult previousFirstCoverInitializedNameError;
};
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);
}
}
void startCoverGrammar(IsolateCoverGrammarContext* grammarContext)
{
ASSERT(grammarContext != nullptr);
grammarContext->previousIsBindingElement = this->context->isBindingElement;
grammarContext->previousIsAssignmentTarget = this->context->isAssignmentTarget;
grammarContext->previousFirstCoverInitializedNameError = this->context->firstCoverInitializedNameError;
this->context->isBindingElement = true;
this->context->isAssignmentTarget = true;
this->context->firstCoverInitializedNameError.reset();
checkRecursiveLimit();
}
void endIsolateCoverGrammar(IsolateCoverGrammarContext* grammarContext)
{
ASSERT(grammarContext != nullptr);
if (UNLIKELY(this->context->firstCoverInitializedNameError)) {
this->throwUnexpectedToken(this->context->firstCoverInitializedNameError);
}
this->context->isBindingElement = grammarContext->previousIsBindingElement;
this->context->isAssignmentTarget = grammarContext->previousIsAssignmentTarget;
this->context->firstCoverInitializedNameError = grammarContext->previousFirstCoverInitializedNameError;
}
void endInheritCoverGrammar(IsolateCoverGrammarContext* grammarContext)
{
ASSERT(grammarContext != nullptr);
this->context->isBindingElement = this->context->isBindingElement && grammarContext->previousIsBindingElement;
this->context->isAssignmentTarget = this->context->isAssignmentTarget && grammarContext->previousIsAssignmentTarget;
if (UNLIKELY(grammarContext->previousFirstCoverInitializedNameError)) {
this->context->firstCoverInitializedNameError = grammarContext->previousFirstCoverInitializedNameError;
}
}
template <class ASTBuilder, typename T>
ALWAYS_INLINE ASTNode isolateCoverGrammar(ASTBuilder& builder, T parseFunction)
{
IsolateCoverGrammarContext grammarContext;
startCoverGrammar(&grammarContext);
ASTNode result = (this->*parseFunction)(builder);
endIsolateCoverGrammar(&grammarContext);
return result;
}
template <class ASTBuilder, typename T>
ALWAYS_INLINE ASTNode isolateCoverGrammarWithFunctor(ASTBuilder& builder, T parseFunction)
{
IsolateCoverGrammarContext grammarContext;
startCoverGrammar(&grammarContext);
ASTNode result = parseFunction();
endIsolateCoverGrammar(&grammarContext);
return result;
}
template <class ASTBuilder, typename T>
ALWAYS_INLINE ASTNode inheritCoverGrammar(ASTBuilder& builder, T parseFunction)
{
IsolateCoverGrammarContext grammarContext;
startCoverGrammar(&grammarContext);
ASTNode result = (this->*parseFunction)(builder);
endInheritCoverGrammar(&grammarContext);
return result;
}
template <class ASTBuilder>
ASTNode finishIdentifier(ASTBuilder& builder, Scanner::ScannerResult* token)
{
ASSERT(token != nullptr);
ASTNode ret;
StringView sv = token->valueStringLiteral(this->scanner);
const auto& a = sv.bufferAccessData();
char16_t firstCh = a.charAt(0);
if (a.length == 1 && firstCh < ESCARGOT_ASCII_TABLE_MAX) {
ret = builder.createIdentifierNode(this->escargotContext->staticStrings().asciiTable[firstCh]);
} else {
if (token->hasAllocatedString) {
ret = builder.createIdentifierNode(AtomicString(this->escargotContext, sv.string()));
} else {
ret = builder.createIdentifierNode(AtomicString(this->escargotContext, sv));
}
}
if (this->trackUsingNames) {
insertUsingName(ret->asIdentifier()->name());
}
return ret;
}
// ECMA-262 12.2 Primary Expressions
template <class ASTBuilder>
ASTNode parsePrimaryExpression(ASTBuilder& builder)
{
MetaNode node = this->createNode();
switch (this->lookahead.type) {
case Token::IdentifierToken: {
if (this->sourceType == SourceType::Module && this->lookahead.valueKeywordKind == AwaitKeyword) {
this->throwUnexpectedToken(this->lookahead);
}
ALLOC_TOKEN(token);
this->nextToken(token);
return this->finalize(node, finishIdentifier(builder, token));
}
case Token::NumericLiteralToken:
case Token::StringLiteralToken:
if (this->context->strict && this->lookahead.octal) {
this->throwUnexpectedToken(this->lookahead, Messages::StrictOctalLiteral);
}
if (this->context->strict && this->lookahead.startWithZero) {
this->throwUnexpectedToken(this->lookahead, Messages::StrictLeadingZeroLiteral);
}
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
{
ALLOC_TOKEN(token);
this->nextToken(token);
// raw = this->getTokenRaw(token);
if (builder.isNodeGenerator()) {
if (token->type == Token::NumericLiteralToken) {
double d = token->valueNumberLiteral(this->scanner);
if (this->context->inLoop || d == 0) {
this->insertNumeralLiteral(Value(d));
}
return this->finalize(node, builder.createLiteralNode(Value(d)));
} else {
return this->finalize(node, builder.createLiteralNode(token->valueStringLiteralForAST(this->scanner)));
}
} else {
return this->finalize(node, builder.createLiteralNode(Value()));
}
}
break;
case Token::BooleanLiteralToken: {
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
ALLOC_TOKEN(token);
this->nextToken(token);
// token.value = (token.value === 'true');
// raw = this->getTokenRaw(token);
{
bool value = token->relatedSource(this->scanner->source) == "true";
if (builder.isNodeGenerator()) {
this->insertNumeralLiteral(Value(value));
}
return this->finalize(node, builder.createLiteralNode(Value(value)));
}
break;
}
case Token::NullLiteralToken: {
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
ALLOC_TOKEN(token);
this->nextToken(token);
// token.value = null;
// raw = this->getTokenRaw(token);
if (builder.isNodeGenerator()) {
this->insertNumeralLiteral(Value(Value::Null));
}
return this->finalize(node, builder.createLiteralNode(Value(Value::Null)));
}
case Token::TemplateToken:
return this->parseTemplateLiteral(builder);
case Token::PunctuatorToken: {
PunctuatorKind value = this->lookahead.valuePunctuatorKind;
switch (value) {
case LeftParenthesis:
this->context->isBindingElement = false;
return this->inheritCoverGrammar(builder, &Parser::parseGroupExpression<ASTBuilder>);
case LeftSquareBracket:
return this->inheritCoverGrammar(builder, &Parser::parseArrayInitializer<ASTBuilder>);
case LeftBrace:
return this->inheritCoverGrammar(builder, &Parser::parseObjectInitializer<ASTBuilder>);
case Divide:
case DivideEqual: {
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
this->scanner->index = this->startMarker.index;
ALLOC_TOKEN(token);
this->nextRegexToken(token);
// raw = this->getTokenRaw(token);
return this->finalize(node, builder.createRegExpLiteralNode(token->valueRegexp.body, token->valueRegexp.flags));
}
default: {
ALLOC_TOKEN(token);
this->nextToken(token);
this->throwUnexpectedToken(*token);
}
}
break;
}
case Token::KeywordToken:
if (!this->context->strict && this->context->allowYield && this->matchKeyword(YieldKeyword)) {
return this->parseIdentifierName(builder);
} else if (!this->context->strict && this->matchKeyword(LetKeyword)) {
ALLOC_TOKEN(token);
this->nextToken(token);
throwUnexpectedToken(*token);
} else {
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
if (this->matchKeyword(FunctionKeyword)) {
return this->parseFunctionExpression(builder);
} else if (this->matchKeyword(ThisKeyword)) {
this->nextToken();
return this->finalize(node, builder.createThisExpressionNode());
} else if (this->matchKeyword(SuperKeyword)) {
throwIfSuperOperationIsNotAllowed();
return this->finalize(node, builder.createSuperExpressionNode(this->lookahead.valuePunctuatorKind == LeftParenthesis));
} else if (this->matchKeyword(ClassKeyword)) {
return this->parseClassExpression(builder);
} else {
ALLOC_TOKEN(token);
this->nextToken(token);
this->throwUnexpectedToken(*token);
}
}
break;
default: {
ALLOC_TOKEN(token);
this->nextToken(token);
this->throwUnexpectedToken(*token);
}
}
ASSERT_NOT_REACHED();
return ASTNode();
}
void validateParam(ParseFormalParametersResult& options, const Scanner::SmallScannerResult& param, AtomicString name)
{
ASSERT(param);
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);
}
template <class ASTBuilder>
ASTNode parseRestElement(ASTBuilder& builder, SmallScannerResultVector& params)
{
MetaNode node = this->createNode();
this->expect(PeriodPeriodPeriod);
ASTNode arg = this->parsePattern(builder, params);
if (this->match(Substitution)) {
this->throwError(Messages::DefaultRestParameter);
}
if (!this->match(RightParenthesis)) {
this->throwError(Messages::ParameterAfterRestParameter);
}
return this->finalize(node, builder.createRestElementNode(arg));
}
template <class ASTBuilder>
ASTNode parseBindingRestElement(ASTBuilder& builder, SmallScannerResultVector& params, KeywordKind kind = KeywordKindEnd, bool isExplicitVariableDeclaration = false)
{
MetaNode node = this->createNode();
this->expect(PeriodPeriodPeriod);
ASTNode arg = this->parsePattern(builder, params, kind, isExplicitVariableDeclaration);
return this->finalize(node, builder.createRestElementNode(arg));
}
// ECMA-262 13.3.3 Destructuring Binding Patterns
template <class ASTBuilder>
ASTNode parseArrayPattern(ASTBuilder& builder, SmallScannerResultVector& params, KeywordKind kind = KeywordKindEnd, bool isExplicitVariableDeclaration = false)
{
MetaNode node = this->createNode();
this->expect(LeftSquareBracket);
ASTNodeVector elements;
while (!this->match(RightSquareBracket)) {
if (this->match(Comma)) {
this->nextToken();
elements.push_back(nullptr);
} else {
if (this->match(PeriodPeriodPeriod)) {
elements.push_back(this->parseBindingRestElement(builder, params, kind, isExplicitVariableDeclaration));
break;
} else {
elements.push_back(this->parsePatternWithDefault(builder, params, kind, isExplicitVariableDeclaration));
}
if (!this->match(RightSquareBracket)) {
this->expect(Comma);
}
}
}
this->expect(RightSquareBracket);
return this->finalize(node, builder.createArrayPatternNode(std::move(elements)));
}
template <class ASTBuilder>
ASTNode parsePropertyPattern(ASTBuilder& builder, SmallScannerResultVector& params, KeywordKind kind = KeywordKindEnd, bool isExplicitVariableDeclaration = false)
{
MetaNode node = this->createNode();
bool computed = false;
bool shorthand = false;
bool method = false;
ASTNode keyNode = nullptr; //'': Node.PropertyKey;
ASTNode valueNode = nullptr; //: Node.PropertyValue;
if (this->lookahead.type == Token::IdentifierToken) {
ALLOC_TOKEN(keyToken);
*keyToken = this->lookahead;
keyNode = this->parseVariableIdentifier(builder);
if (this->match(Substitution)) {
params.push_back(*keyToken);
shorthand = true;
this->nextToken();
ASTNode expr = this->parseAssignmentExpression<ASTBuilder, false>(builder);
valueNode = this->finalize(this->startNode(keyToken), builder.createAssignmentPatternNode(keyNode, expr));
} else if (!this->match(Colon)) {
params.push_back(*keyToken);
if (isExplicitVariableDeclaration) {
ASSERT(kind == KeywordKind::VarKeyword || kind == KeywordKind::LetKeyword || kind == KeywordKind::ConstKeyword);
addDeclaredNameIntoContext(keyNode->asIdentifier()->name(), this->lexicalBlockIndex, kind, isExplicitVariableDeclaration);
}
shorthand = true;
valueNode = keyNode;
} else {
this->expect(Colon);
valueNode = this->parsePatternWithDefault(builder, params, kind, isExplicitVariableDeclaration);
}
} else {
computed = this->match(LeftSquareBracket);
StringView keyString;
keyNode = this->parseObjectPropertyKey(builder, keyString);
this->expect(Colon);
valueNode = this->parsePatternWithDefault(builder, params, kind, isExplicitVariableDeclaration);
}
return this->finalize(node, builder.createPropertyNode(keyNode, valueNode, PropertyNode::Kind::Init, computed, shorthand));
}
template <class ASTBuilder>
ASTNode parseObjectPattern(ASTBuilder& builder, SmallScannerResultVector& params, KeywordKind kind = KeywordKindEnd, bool isExplicitVariableDeclaration = false)
{
MetaNode node = this->createNode();
ASTNodeVector properties;
this->expect(LeftBrace);
while (!this->match(RightBrace)) {
properties.push_back(this->parsePropertyPattern(builder, params, kind, isExplicitVariableDeclaration));
if (!this->match(RightBrace)) {
this->expect(Comma);
}
}
this->expect(RightBrace);
return this->finalize(node, builder.createObjectPatternNode(std::move(properties)));
}
template <class ASTBuilder>
ASTNode parsePattern(ASTBuilder& builder, SmallScannerResultVector& params, KeywordKind kind = KeywordKindEnd, bool isExplicitVariableDeclaration = false)
{
if (this->match(LeftSquareBracket)) {
return this->parseArrayPattern(builder, params, kind, isExplicitVariableDeclaration);
} else if (this->match(LeftBrace)) {
return this->parseObjectPattern(builder, params, kind, isExplicitVariableDeclaration);
} else {
if (this->matchKeyword(LetKeyword) && (kind == ConstKeyword || kind == LetKeyword)) {
this->throwUnexpectedToken(this->lookahead, Messages::UnexpectedToken);
}
params.push_back(this->lookahead);
return this->parseVariableIdentifier(builder, kind, isExplicitVariableDeclaration);
}
}
template <class ASTBuilder>
ASTNode parsePatternWithDefault(ASTBuilder& builder, SmallScannerResultVector& params, KeywordKind kind = KeywordKindEnd, bool isExplicitVariableDeclaration = false)
{
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
ASTNode pattern = this->parsePattern(builder, params, kind, isExplicitVariableDeclaration);
if (this->match(PunctuatorKind::Substitution)) {
this->nextToken();
const bool previousAllowYield = this->context->allowYield;
ASTNode right = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
return this->finalize(this->startNode(startToken), builder.createAssignmentPatternNode(pattern, right));
}
return pattern;
}
template <class ASTBuilder>
ASTNode parseFormalParameter(ASTBuilder& builder, ParseFormalParametersResult& options, bool& end)
{
ASTNode param = nullptr;
bool trackUsingNamesBefore = this->trackUsingNames;
this->trackUsingNames = false;
SmallScannerResultVector params;
ALLOC_TOKEN(token);
*token = this->lookahead;
if (token->type == Token::PunctuatorToken && token->valuePunctuatorKind == PunctuatorKind::PeriodPeriodPeriod) {
param = this->parseRestElement(builder, params);
} else {
param = this->parsePatternWithDefault(builder, params);
}
for (size_t i = 0; i < params.size(); i++) {
AtomicString as(this->escargotContext, params[i].relatedSource(this->scanner->source));
this->validateParam(options, params[i], as);
}
options.params.push_back(builder.convertToParameterSyntaxNode(param));
this->trackUsingNames = trackUsingNamesBefore;
end = this->match(PunctuatorKind::RightParenthesis);
return param;
}
template <class ASTBuilder>
void parseFormalParameters(ASTBuilder& builder, ParseFormalParametersResult& options, Scanner::SmallScannerResult* firstRestricted = nullptr)
{
// parseFormalParameters only scans the parameter list of function
// should be invoked only during the global parsing (program)
ASSERT(!builder.isNodeGenerator() && !this->isParsingSingleFunction);
const bool previousInParameterParsing = this->context->inParameterParsing;
this->context->inParameterParsing = true;
if (firstRestricted) {
options.firstRestricted = *firstRestricted;
}
bool end = false;
if (!this->match(RightParenthesis)) {
options.paramSet.clear();
while (this->startMarker.index < this->scanner->length) {
this->parseFormalParameter(builder, options, end);
if (end) {
break;
}
this->expect(Comma);
}
}
this->expect(RightParenthesis);
this->context->inParameterParsing = previousInParameterParsing;
}
// ECMA-262 12.2.5 Array Initializer
template <class ASTBuilder, bool checkLeftHasRestrictedWord>
ASTNode parseSpreadElement(ASTBuilder& builder)
{
MetaNode node = this->createNode();
this->expect(PunctuatorKind::PeriodPeriodPeriod);
ASTNode arg = this->inheritCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, checkLeftHasRestrictedWord>);
if (arg->isAssignmentOperation()) {
this->throwError(Messages::DefaultRestParameter);
}
return this->finalize(node, builder.createSpreadElementNode(arg));
}
template <class ASTBuilder>
ASTNode parseArrayInitializer(ASTBuilder& builder)
{
MetaNode node = this->createNode();
ASTNodeVector elements;
this->expect(LeftSquareBracket);
bool hasSpreadElement = false;
while (!this->match(RightSquareBracket)) {
if (this->match(Comma)) {
this->nextToken();
elements.push_back(nullptr);
} else if (this->match(PeriodPeriodPeriod)) {
elements.push_back(this->parseSpreadElement<ASTBuilder, true>(builder));
hasSpreadElement = true;
if (!this->match(RightSquareBracket)) {
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
this->expect(Comma);
}
} else {
elements.push_back(this->inheritCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, true>));
if (!this->match(RightSquareBracket)) {
this->expect(Comma);
}
}
}
this->expect(RightSquareBracket);
return this->finalize(node, builder.createArrayExpressionNode(std::move(elements), AtomicString(), nullptr, hasSpreadElement, false));
}
// ECMA-262 12.2.6 Object Initializer
template <class ASTBuilder>
ASTNode parsePropertyMethod(ASTBuilder& builder, ParseFormalParametersResult& params)
{
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
const bool previousStrict = this->context->strict;
ASTNode body = this->isolateCoverGrammar(builder, &Parser::parseFunctionSourceElements<ASTBuilder>);
if (this->context->strict && params.firstRestricted) {
this->throwUnexpectedToken(params.firstRestricted, params.message);
}
if (this->context->strict && params.stricted) {
this->throwUnexpectedToken(params.stricted, params.message);
}
this->context->strict = previousStrict;
return body;
}
template <class ASTBuilder>
ASTNode parsePropertyMethodFunction(ASTBuilder& builder, bool allowSuperCall)
{
MetaNode node = this->createNode();
const bool isGenerator = false;
if (tryToSkipFunctionParsing()) {
return this->finalize(node, builder.createFunctionExpressionNode(isGenerator, this->subCodeBlockIndex, AtomicString()));
}
const bool previousAllowYield = this->context->allowYield;
const bool previousAllowSuperCall = this->context->allowSuperCall;
const bool previousAllowSuperProperty = this->context->allowSuperProperty;
const bool previousInArrowFunction = this->context->inArrowFunction;
this->context->allowYield = true;
this->context->inArrowFunction = false;
this->context->allowSuperProperty = true;
if (allowSuperCall) {
this->context->allowSuperCall = true;
}
this->expect(LeftParenthesis);
BEGIN_FUNCTION_SCANNING(AtomicString());
ParseFormalParametersResult params;
this->parseFormalParameters(newBuilder, params);
extractNamesFromFunctionParams(params);
this->parsePropertyMethod(newBuilder, params);
this->context->allowYield = previousAllowYield;
this->context->inArrowFunction = previousInArrowFunction;
this->context->allowSuperProperty = previousAllowSuperProperty;
this->context->allowSuperCall = previousAllowSuperCall;
this->currentScopeContext->m_paramsStartLOC.index = node.index;
this->currentScopeContext->m_paramsStartLOC.column = node.column;
this->currentScopeContext->m_paramsStartLOC.line = node.line;
this->currentScopeContext->m_allowSuperProperty = true;
if (allowSuperCall) {
this->currentScopeContext->m_allowSuperCall = true;
}
this->currentScopeContext->m_nodeType = ASTNodeType::FunctionExpression;
this->currentScopeContext->m_isGenerator = isGenerator;
END_FUNCTION_SCANNING();
return this->finalize(node, builder.createFunctionExpressionNode(isGenerator, this->subCodeBlockIndex, AtomicString()));
}
template <class ASTBuilder>
ASTNode parseObjectPropertyKey(ASTBuilder& builder, StringView& keyString)
{
MetaNode node = this->createNode();
ALLOC_TOKEN(token);
this->nextToken(token);
ASTNode key = nullptr;
switch (token->type) {
case Token::NumericLiteralToken:
case Token::StringLiteralToken:
if (this->context->strict && token->octal) {
this->throwUnexpectedToken(*token, Messages::StrictOctalLiteral);
}
if (this->context->strict && this->lookahead.startWithZero) {
this->throwUnexpectedToken(this->lookahead, Messages::StrictLeadingZeroLiteral);
}
// const raw = this->getTokenRaw(token);
{
Value v;
if (builder.isNodeGenerator()) {
if (token->type == Token::NumericLiteralToken) {
double d = token->valueNumberLiteral(this->scanner);
if (this->context->inLoop || d == 0) {
this->insertNumeralLiteral(Value(d));
}
v = Value(d);
} else {
keyString = token->valueStringLiteral(this->scanner);
v = Value(token->valueStringLiteralForAST(this->scanner));
}
} else {
if (token->type == Token::StringLiteralToken) {
keyString = token->valueStringLiteral(this->scanner);
}
}
key = this->finalize(node, builder.createLiteralNode(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(builder, token));
keyString = StringView(key->asIdentifier()->name().string());
this->trackUsingNames = trackUsingNamesBefore;
break;
}
case Token::PunctuatorToken:
if (token->valuePunctuatorKind == LeftSquareBracket) {
key = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
this->expect(RightSquareBracket);
} else {
this->throwUnexpectedToken(*token);
}
break;
default:
this->throwUnexpectedToken(*token);
}
return key;
}
template <class ASTBuilder>
ASTNode parseObjectProperty(ASTBuilder& builder, bool& hasProto) //: Node.Property
{
ALLOC_TOKEN(token);
*token = this->lookahead;
MetaNode node = this->createNode();
PropertyNode::Kind kind;
ASTNode keyNode = nullptr; //'': Node.PropertyKey;
ASTNode valueNode = nullptr; //: Node.PropertyValue;
StringView keyString;
bool computed = false;
bool method = false;
bool shorthand = false;
bool isProto = false;
if (token->type == Token::IdentifierToken) {
this->nextToken();
keyNode = this->finalize(node, finishIdentifier(builder, token));
} else if (this->match(PunctuatorKind::Multiply)) {
this->nextToken();
} else {
computed = this->match(LeftSquareBracket);
keyNode = this->parseObjectPropertyKey(builder, keyString);
}
bool lookaheadPropertyKey = this->qualifiedPropertyName(&this->lookahead);
bool isGet = false;
bool isSet = false;
bool needImplictName = false;
if (token->type == Token::IdentifierToken && lookaheadPropertyKey) {
StringView sv = token->valueStringLiteral(this->scanner);
const auto& d = sv.bufferAccessData();
if (d.length == 3) {
if (d.equalsSameLength("get")) {
isGet = true;
} else if (d.equalsSameLength("set")) {
isSet = true;
}
}
}
if (isGet) {
kind = PropertyNode::Kind::Get;
computed = this->match(LeftSquareBracket);
keyNode = this->parseObjectPropertyKey(builder, keyString);
this->context->allowYield = false;
valueNode = this->parseGetterMethod(builder);
} else if (isSet) {
kind = PropertyNode::Kind::Set;
computed = this->match(LeftSquareBracket);
keyNode = this->parseObjectPropertyKey(builder, keyString);
valueNode = this->parseSetterMethod(builder);
} else if (token->type == Token::PunctuatorToken && token->valuePunctuatorKind == PunctuatorKind::Multiply && lookaheadPropertyKey) {
kind = PropertyNode::Kind::Init;
computed = this->match(LeftSquareBracket);
keyNode = this->parseObjectPropertyKey(builder, keyString);
valueNode = this->parseGeneratorMethod(builder);
method = true;
} else {
if (!keyNode) {
this->throwUnexpectedToken(this->lookahead);
}
kind = PropertyNode::Kind::Init;
if (this->match(PunctuatorKind::Colon)) {
// FIXME check !this->isParsingSingleFunction
isProto = !this->isParsingSingleFunction && this->isPropertyKey(keyNode, keyString, "__proto__");
if (!computed && isProto) {
if (hasProto) {
this->throwError(Messages::DuplicateProtoProperty);
}
hasProto = true;
}
this->nextToken();
ASTNodeType type = ASTNodeType::ASTNodeTypeError;
valueNode = this->inheritCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, true>);
if (valueNode) {
type = valueNode->type();
}
if ((type == ASTNodeType::FunctionExpression || type == ASTNodeType::ArrowFunctionExpression) && this->lastPoppedScopeContext->m_functionName == AtomicString()) {
needImplictName = true;
}
} else if (this->match(LeftParenthesis)) {
valueNode = this->parsePropertyMethodFunction(builder, false);
method = true;
} else {
if (token->type != Token::IdentifierToken) {
if (token->type == Token::KeywordToken && token->valueKeywordKind == KeywordKind::YieldKeyword) {
// yield is a valid Identifier in AssignmentProperty outside of strict mode and generator functions
if (!this->context->allowYield) {
this->throwUnexpectedToken(*token);
}
} else if (token->type == Token::KeywordToken && token->valueKeywordKind == KeywordKind::LetKeyword) {
// In non-strict mode, let is a valid Identifier
if (this->context->strict) {
this->throwUnexpectedToken(*token);
}
} else {
ALLOC_TOKEN(token);
this->nextToken(token);
this->throwUnexpectedToken(*token);
}
}
if (this->match(Substitution)) {
this->context->firstCoverInitializedNameError = this->lookahead;
this->nextToken();
shorthand = true;
ASTNode init = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
valueNode = this->finalize(node, builder.createAssignmentPatternNode(keyNode, init));
} else {
shorthand = true;
valueNode = keyNode;
}
}
}
// check if restricted words are used as target in array/object initializer
if (shorthand && this->context->strict && token->type == Token::IdentifierToken) {
AtomicString name;
name = keyNode->asIdentifier()->name();
if (this->scanner->isRestrictedWord(name)) {
this->context->hasRestrictedWordInArrayOrObjectInitializer = true;
}
}
if (!this->isParsingSingleFunction && (method || isGet || isSet || needImplictName)) {
AtomicString as;
if (!computed && keyNode->isIdentifier()) {
as = keyNode->asIdentifier()->name();
}
this->lastPoppedScopeContext->m_functionName = as;
if (needImplictName) {
this->lastPoppedScopeContext->m_hasImplictFunctionName = true;
} else {
this->lastPoppedScopeContext->m_isClassMethod = true;
}
}
return this->finalize(node, builder.createPropertyNode(keyNode, valueNode, kind, computed, shorthand));
}
template <class ASTBuilder>
ASTNode parseObjectInitializer(ASTBuilder& builder)
{
this->expect(LeftBrace);
MetaNode node = this->createNode();
ASTNodeVector properties;
bool hasProto = false;
while (!this->match(RightBrace)) {
properties.push_back(this->parseObjectProperty(builder, hasProto));
if (!this->match(RightBrace)) {
this->expectCommaSeparator();
}
}
this->expect(RightBrace);
return this->finalize(node, builder.createObjectExpressionNode(std::move(properties)));
}
// ECMA-262 12.2.9 Template Literals
// FIXME
TemplateElement* parseTemplateHead()
{
ASSERT(this->lookahead.type == Token::TemplateToken);
ALLOC_TOKEN(token);
this->nextToken(token);
MetaNode node = this->createNode();
TemplateElement* tm = new TemplateElement();
tm->value = token->valueTemplate->valueCooked;
tm->valueRaw = token->valueTemplate->valueRaw;
tm->tail = token->valueTemplate->tail;
return tm;
}
// FIXME
TemplateElement* parseTemplateElement()
{
if (!this->match(PunctuatorKind::RightBrace)) {
this->throwUnexpectedToken(this->lookahead);
}
// Re-scan the current token (right brace) as a template string.
this->scanner->scanTemplate(&this->lookahead);
ALLOC_TOKEN(token);
this->nextToken(token);
MetaNode node = this->createNode();
TemplateElement* tm = new TemplateElement();
tm->value = token->valueTemplate->valueCooked;
tm->valueRaw = token->valueTemplate->valueRaw;
tm->tail = token->valueTemplate->tail;
return tm;
}
template <class ASTBuilder>
ASTNode parseTemplateLiteral(ASTBuilder& builder)
{
MetaNode node = this->createNode();
ASTNodeVector expressions;
TemplateElementVector* quasis = new (GC) TemplateElementVector;
quasis->push_back(this->parseTemplateHead());
while (!quasis->back()->tail) {
expressions.push_back(this->parseExpression(builder));
TemplateElement* quasi = this->parseTemplateElement();
quasis->push_back(quasi);
}
return this->finalize(node, builder.createTemplateLiteralNode(quasis, std::move(expressions)));
}
template <class ASTBuilder>
ASTNode parseGroupExpression(ASTBuilder& builder)
{
ASTNode exprNode = nullptr;
this->expect(LeftParenthesis);
if (this->match(RightParenthesis)) {
this->nextToken();
if (!this->match(Arrow)) {
this->expect(Arrow);
}
exprNode = this->finalize(this->createNode(), builder.createArrowParameterPlaceHolderNode());
} else {
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
SmallScannerResultVector params;
if (this->match(PeriodPeriodPeriod)) {
exprNode = this->parseRestElement(builder, params);
this->expect(RightParenthesis);
if (!this->match(Arrow)) {
this->expect(Arrow);
}
exprNode = this->finalize(this->createNode(), builder.createArrowParameterPlaceHolderNode(exprNode));
} else {
bool arrow = false;
this->context->isBindingElement = true;
exprNode = this->inheritCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
if (this->match(Comma)) {
ASTNodeVector expressions;
this->context->isAssignmentTarget = false;
expressions.push_back(exprNode);
while (this->startMarker.index < this->scanner->length) {
if (!this->match(Comma)) {
break;
}
this->nextToken();
if (this->match(RightParenthesis)) {
this->nextToken();
for (size_t i = 0; i < expressions.size(); i++) {
expressions[i] = builder.reinterpretExpressionAsPattern(expressions[i]);
}
arrow = true;
exprNode = this->finalize(this->createNode(), builder.createArrowParameterPlaceHolderNode(std::move(expressions)));
} else if (this->match(PeriodPeriodPeriod)) {
if (!this->context->isBindingElement) {
this->throwUnexpectedToken(this->lookahead);
}
expressions.push_back(this->parseRestElement(builder, params));
this->expect(RightParenthesis);
if (!this->match(Arrow)) {
this->expect(Arrow);
}
this->context->isBindingElement = false;
for (size_t i = 0; i < expressions.size(); i++) {
expressions[i] = builder.reinterpretExpressionAsPattern(expressions[i]);
}
arrow = true;
exprNode = this->finalize(this->createNode(), builder.createArrowParameterPlaceHolderNode(std::move(expressions)));
} else {
expressions.push_back(this->inheritCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>));
}
if (arrow) {
break;
}
}
if (!arrow) {
exprNode = this->finalize(this->startNode(startToken), builder.createSequenceExpressionNode(std::move(expressions)));
}
}
if (!arrow) {
this->expect(RightParenthesis);
if (this->match(Arrow)) {
if (exprNode->type() == Identifier && exprNode->asIdentifier()->name() == "yield") {
arrow = true;
exprNode = this->finalize(this->createNode(), builder.createArrowParameterPlaceHolderNode(exprNode));
}
if (!arrow) {
if (!this->context->isBindingElement) {
this->throwUnexpectedToken(this->lookahead);
}
if (builder.isNodeGenerator() && (exprNode->type() == SequenceExpression)) {
ASTNodeVector& expressions = exprNode->asSequenceExpression()->expressions();
for (size_t i = 0; i < expressions.size(); i++) {
expressions[i] = builder.reinterpretExpressionAsPattern(expressions[i]);
}
} else {
exprNode = builder.reinterpretExpressionAsPattern(exprNode);
}
ASTNodeVector params;
if (builder.isNodeGenerator() && (exprNode->type() == SequenceExpression)) {
params = exprNode->asSequenceExpression()->expressions();
} else {
params.push_back(exprNode);
}
exprNode = this->finalize(this->createNode(), builder.createArrowParameterPlaceHolderNode(std::move(params)));
}
}
this->context->isBindingElement = false;
}
}
}
return exprNode;
}
// ECMA-262 12.3 Left-Hand-Side Expressions
template <class ASTBuilder>
ASTNodeVector parseArguments(ASTBuilder& builder)
{
this->expect(LeftParenthesis);
ASTNodeVector args;
if (!this->match(RightParenthesis)) {
while (true) {
ASTNode expr = nullptr;
if (this->match(PeriodPeriodPeriod)) {
expr = this->parseSpreadElement<ASTBuilder, false>(builder);
} else {
expr = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
}
args.push_back(expr);
if (this->match(RightParenthesis)) {
break;
}
this->expectCommaSeparator();
}
}
this->expect(RightParenthesis);
if (UNLIKELY(args.size() > 65535)) {
this->throwError("too many arguments in call");
}
return args;
}
bool isIdentifierName(Scanner::ScannerResult* token)
{
ASSERT(token != nullptr);
return token->type == Token::IdentifierToken || token->type == Token::KeywordToken || token->type == Token::BooleanLiteralToken || token->type == Token::NullLiteralToken;
}
template <class ASTBuilder>
ASTNode parseIdentifierName(ASTBuilder& builder)
{
MetaNode node = this->createNode();
ALLOC_TOKEN(token);
this->nextToken(token);
if (!this->isIdentifierName(token)) {
this->throwUnexpectedToken(*token);
}
return this->finalize(node, finishIdentifier(builder, token));
}
template <class ASTBuilder>
ASTNode parseNewExpression(ASTBuilder& builder)
{
this->nextToken();
if (this->match(Period)) {
this->nextToken();
if (this->lookahead.type == Token::IdentifierToken && this->context->inFunctionBody && this->lookahead.relatedSource(this->scanner->source) == "target") {
this->nextToken();
this->currentScopeContext->m_hasSuperOrNewTarget = true;
MetaNode node = this->createNode();
return this->finalize(node, builder.createMetaPropertyNode());
} else {
this->throwUnexpectedToken(this->lookahead);
}
}
MetaNode node = this->createNode();
ASTNode callee = this->isolateCoverGrammar(builder, &Parser::parseLeftHandSideExpression<ASTBuilder>);
ASTNodeVector args;
if (this->match(LeftParenthesis)) {
args = this->parseArguments(builder);
}
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
return this->finalize(node, builder.createNewExpressionNode(callee, std::move(args)));
}
template <class ASTBuilder>
ASTNode parseLeftHandSideExpressionAllowCall(ASTBuilder& builder)
{
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
bool previousAllowIn = this->context->allowIn;
this->context->allowIn = true;
ASTNode exprNode = nullptr;
if (this->context->inFunctionBody && this->matchKeyword(SuperKeyword)) {
throwIfSuperOperationIsNotAllowed();
this->currentScopeContext->m_hasSuperOrNewTarget = true;
exprNode = this->finalize(this->createNode(), builder.createSuperExpressionNode(this->lookahead.valuePunctuatorKind == LeftParenthesis));
} else if (this->matchKeyword(NewKeyword)) {
exprNode = this->inheritCoverGrammar(builder, &Parser::parseNewExpression<ASTBuilder>);
} else {
exprNode = this->inheritCoverGrammar(builder, &Parser::parsePrimaryExpression<ASTBuilder>);
}
while (true) {
bool isPunctuatorTokenLookahead = this->lookahead.type == Token::PunctuatorToken;
if (isPunctuatorTokenLookahead) {
if (this->lookahead.valuePunctuatorKind == Period) {
this->context->isBindingElement = false;
this->context->isAssignmentTarget = true;
this->nextToken();
bool trackUsingNamesBefore = this->trackUsingNames;
this->trackUsingNames = false;
ASTNode property = this->parseIdentifierName(builder);
exprNode = this->finalize(this->startNode(startToken), builder.createMemberExpressionNode(exprNode, property, true));
this->trackUsingNames = trackUsingNamesBefore;
} else if (this->lookahead.valuePunctuatorKind == LeftParenthesis) {
this->context->isBindingElement = false;
this->context->isAssignmentTarget = false;
// check callee of CallExpressionNode
if (exprNode->isIdentifier() && exprNode->asIdentifier()->name() == escargotContext->staticStrings().eval) {
this->currentScopeContext->m_hasEval = true;
}
exprNode = this->finalize(this->startNode(startToken), builder.createCallExpressionNode(exprNode, this->parseArguments(builder)));
} else if (this->lookahead.valuePunctuatorKind == LeftSquareBracket) {
this->context->isBindingElement = false;
this->context->isAssignmentTarget = true;
this->nextToken();
ASTNode property = this->isolateCoverGrammar(builder, &Parser::parseExpression<ASTBuilder>);
exprNode = this->finalize(this->startNode(startToken), builder.createMemberExpressionNode(exprNode, property, false));
this->expect(RightSquareBracket);
} else {
break;
}
} else if (this->lookahead.type == Token::TemplateToken && this->lookahead.valueTemplate->head) {
ASTNode quasi = this->parseTemplateLiteral(builder);
// FIXME convertTaggedTemplateExpressionToCallExpression
exprNode = builder.convertTaggedTemplateExpressionToCallExpression(this->finalize(this->startNode(startToken), builder.createTaggedTemplateExpressionNode(exprNode, quasi)), this->currentScopeContext, escargotContext->staticStrings().raw);
} else {
break;
}
}
this->context->allowIn = previousAllowIn;
return exprNode;
}
template <class ASTBuilder>
ASTNode parseSuper(ASTBuilder& builder)
{
MetaNode node = this->createNode();
this->expectKeyword(SuperKeyword);
if (!this->match(LeftSquareBracket) && !this->match(Period)) {
this->throwUnexpectedToken(this->lookahead);
}
this->currentScopeContext->m_hasSuperOrNewTarget = true;
return this->finalize(node, builder.createSuperExpressionNode(false));
}
template <class ASTBuilder>
ASTNode parseLeftHandSideExpression(ASTBuilder& builder)
{
// assert(this->context->allowIn, 'callee of new expression always allow in keyword.');
ASSERT(this->context->allowIn);
ASTNode exprNode = nullptr;
if (this->matchKeyword(SuperKeyword) && this->context->inFunctionBody) {
exprNode = this->parseSuper(builder);
} else if (this->matchKeyword(NewKeyword)) {
exprNode = this->inheritCoverGrammar(builder, &Parser::parseNewExpression<ASTBuilder>);
} else {
exprNode = this->inheritCoverGrammar(builder, &Parser::parsePrimaryExpression<ASTBuilder>);
}
MetaNode node = this->startNode(&this->lookahead);
while (true) {
if (this->match(LeftSquareBracket)) {
this->context->isBindingElement = false;
this->context->isAssignmentTarget = true;
this->expect(LeftSquareBracket);
ASTNode property = this->isolateCoverGrammar(builder, &Parser::parseExpression<ASTBuilder>);
exprNode = this->finalize(node, builder.createMemberExpressionNode(exprNode, property, false));
this->expect(RightSquareBracket);
} else if (this->match(Period)) {
this->context->isBindingElement = false;
this->context->isAssignmentTarget = true;
this->expect(Period);
bool trackUsingNamesBefore = this->trackUsingNames;
this->trackUsingNames = false;
ASTNode property = this->parseIdentifierName(builder);
exprNode = this->finalize(node, builder.createMemberExpressionNode(exprNode, property, true));
this->trackUsingNames = trackUsingNamesBefore;
} else if (this->lookahead.type == Token::TemplateToken && this->lookahead.valueTemplate->head) {
ASTNode quasi = this->parseTemplateLiteral(builder);
// FIXME convertTaggedTemplateExpressionToCallExpression
exprNode = builder.convertTaggedTemplateExpressionToCallExpression(this->finalize(node, builder.createTaggedTemplateExpressionNode(exprNode, quasi)), this->currentScopeContext, escargotContext->staticStrings().raw);
} else {
break;
}
}
return exprNode;
}
// ECMA-262 12.4 Update Expressions
template <class ASTBuilder>
ASTNode parseUpdateExpression(ASTBuilder& builder)
{
ASTNode exprNode = nullptr;
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
if (this->match(PlusPlus) || this->match(MinusMinus)) {
bool isPlus = this->match(PlusPlus);
ALLOC_TOKEN(token);
this->nextToken(token);
exprNode = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression<ASTBuilder>);
if (exprNode->isLiteral() || exprNode->type() == ASTNodeType::ThisExpression) {
this->throwError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
}
if (this->context->strict && exprNode->type() == Identifier && this->scanner->isRestrictedWord(exprNode->asIdentifier()->name())) {
this->throwError(Messages::StrictLHSPrefix);
}
if (!this->context->isAssignmentTarget && this->context->strict) {
this->throwError(Messages::InvalidLHSInAssignment);
}
MetaNode node = this->startNode(startToken);
if (isPlus) {
exprNode = this->finalize(node, builder.createUpdateExpressionIncrementPrefixNode(exprNode));
} else {
exprNode = this->finalize(node, builder.createUpdateExpressionDecrementPrefixNode(exprNode));
}
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
} else {
exprNode = this->inheritCoverGrammar(builder, &Parser::parseLeftHandSideExpressionAllowCall<ASTBuilder>);
if (!this->hasLineTerminator && this->lookahead.type == Token::PunctuatorToken && (this->match(PlusPlus) || this->match(MinusMinus))) {
bool isPlus = this->match(PlusPlus);
if (exprNode->isLiteral() || exprNode->type() == ASTNodeType::ThisExpression) {
this->throwError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
}
if (this->context->strict && exprNode->isIdentifier() && this->scanner->isRestrictedWord(exprNode->asIdentifier()->name())) {
this->throwError(Messages::StrictLHSPostfix);
}
if (!this->context->isAssignmentTarget && this->context->strict) {
this->throwError(Messages::InvalidLHSInAssignment);
}
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
this->nextToken();
if (isPlus) {
exprNode = this->finalize(this->startNode(startToken), builder.createUpdateExpressionIncrementPostfixNode(exprNode));
} else {
exprNode = this->finalize(this->startNode(startToken), builder.createUpdateExpressionDecrementPostfixNode(exprNode));
}
}
}
return exprNode;
}
// ECMA-262 12.5 Unary Operators
template <class ASTBuilder>
ASTNode parseUnaryExpression(ASTBuilder& builder)
{
if (this->lookahead.type == Token::PunctuatorToken) {
auto punctuatorsKind = this->lookahead.valuePunctuatorKind;
ASTNode exprNode = nullptr;
if (punctuatorsKind == Plus) {
this->nextToken();
MetaNode node = this->startNode(&this->lookahead);
ASTNode subExpr = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression<ASTBuilder>);
exprNode = this->finalize(node, builder.createUnaryExpressionPlusNode(subExpr));
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
return exprNode;
} else if (punctuatorsKind == Minus) {
this->nextToken();
MetaNode node = this->startNode(&this->lookahead);
ASTNode subExpr = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression<ASTBuilder>);
exprNode = this->finalize(node, builder.createUnaryExpressionMinusNode(subExpr));
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
return exprNode;
} else if (punctuatorsKind == Wave) {
this->nextToken();
MetaNode node = this->startNode(&this->lookahead);
ASTNode subExpr = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression<ASTBuilder>);
exprNode = this->finalize(node, builder.createUnaryExpressionBitwiseNotNode(subExpr));
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
return exprNode;
} else if (punctuatorsKind == ExclamationMark) {
this->nextToken();
MetaNode node = this->startNode(&this->lookahead);
ASTNode subExpr = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression<ASTBuilder>);
exprNode = this->finalize(node, builder.createUnaryExpressionLogicalNotNode(subExpr));
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
return exprNode;
}
}
bool isKeyword = this->lookahead.type == Token::KeywordToken;
if (isKeyword) {
ASTNode exprNode = nullptr;
if (this->lookahead.valueKeywordKind == DeleteKeyword) {
this->nextToken();
MetaNode node = this->startNode(&this->lookahead);
ASTNode subExpr = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression<ASTBuilder>);
if (this->context->strict && subExpr->isIdentifier()) {
this->throwError(Messages::StrictDelete);
}
exprNode = this->finalize(node, builder.createUnaryExpressionDeleteNode(subExpr));
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
return exprNode;
} else if (this->lookahead.valueKeywordKind == VoidKeyword) {
this->nextToken();
MetaNode node = this->startNode(&this->lookahead);
ASTNode subExpr = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression<ASTBuilder>);
exprNode = this->finalize(node, builder.createUnaryExpressionVoidNode(subExpr));
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
return exprNode;
} else if (this->lookahead.valueKeywordKind == TypeofKeyword) {
this->nextToken();
MetaNode node = this->startNode(&this->lookahead);
ASTNode subExpr = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression<ASTBuilder>);
exprNode = this->finalize(node, builder.createUnaryExpressionTypeOfNode(subExpr));
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
return exprNode;
}
}
return this->parseUpdateExpression(builder);
}
template <class ASTBuilder>
ASTNode parseExponentiationExpression(ASTBuilder& builder)
{
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
ASTNode expr = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression<ASTBuilder>);
// 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(const Scanner::ScannerResult* token)
{
ASSERT(token != nullptr);
if (LIKELY(token->type == Token::PunctuatorToken)) {
switch (token->valuePunctuatorKind) {
case Substitution:
return 0;
case LogicalOr:
return 1;
case LogicalAnd:
return 2;
case BitwiseOr:
return 3;
case BitwiseXor:
return 4;
case BitwiseAnd:
return 5;
case Equal:
return 6;
case NotEqual:
return 6;
case StrictEqual:
return 6;
case NotStrictEqual:
return 6;
case RightInequality:
return 7;
case LeftInequality:
return 7;
case RightInequalityEqual:
return 7;
case LeftInequalityEqual:
return 7;
case LeftShift:
return 8;
case RightShift:
return 8;
case UnsignedRightShift:
return 8;
case Plus:
return 9;
case Minus:
return 9;
case Multiply:
return 11;
case Divide:
return 11;
case Mod:
return 11;
default:
return 0;
}
} else if (token->type == Token::KeywordToken) {
if (token->valueKeywordKind == InKeyword) {
return this->context->allowIn ? 7 : 0;
} else if (token->valueKeywordKind == InstanceofKeyword) {
return 7;
}
} else {
return 0;
}
return 0;
}
template <class ASTBuilder>
ASTNode parseBinaryExpression(ASTBuilder& builder)
{
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
ASTNode expr = this->inheritCoverGrammar(builder, &Parser::parseExponentiationExpression<ASTBuilder>);
ALLOC_TOKEN(token);
*token = this->lookahead;
int prec = this->binaryPrecedence(token);
if (prec > 0) {
this->nextToken();
token->prec = prec;
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
typedef VectorWithInlineStorage<8, Scanner::SmallScannerResult, std::allocator<Scanner::SmallScannerResult>> SmallScannerResultVectorWithInlineStorage;
SmallScannerResultVectorWithInlineStorage markers;
markers.push_back(*startToken);
markers.push_back(this->lookahead);
ASTNode left = expr;
ASTNode right = this->isolateCoverGrammar(builder, &Parser::parseExponentiationExpression<ASTBuilder>);
VectorWithInlineStorage<8, ASTNode, std::allocator<ASTNode>> stack;
SmallScannerResultVectorWithInlineStorage tokenStack;
stack.push_back(left);
stack.push_back(right);
tokenStack.push_back(*token);
while (true) {
prec = this->binaryPrecedence(&this->lookahead);
if (prec <= 0) {
break;
}
// Reduce: make a binary expression from the three topmost entries.
while ((stack.size() > 1) && (prec <= tokenStack.back().prec)) {
right = stack.back();
stack.pop_back();
Scanner::SmallScannerResult operator_ = tokenStack.back();
tokenStack.pop_back();
left = stack.back();
stack.pop_back();
markers.pop_back();
MetaNode node = this->startNode(&markers.back());
auto e = this->finalize(node, finishBinaryExpression(builder, left, right, &operator_));
stack.push_back(e);
}
// Shift.
this->nextToken(token);
token->prec = prec;
tokenStack.push_back(*token);
markers.push_back(this->lookahead);
auto e = this->isolateCoverGrammar(builder, &Parser::parseExponentiationExpression<ASTBuilder>);
stack.push_back(e);
}
// Final reduce to clean-up the stack.
size_t i = stack.size() - 1;
expr = stack[i];
markers.pop_back();
while (i > 0) {
MetaNode node = this->startNode(&markers.back());
expr = this->finalize(node, finishBinaryExpression(builder, stack[i - 1], expr, &tokenStack.back()));
markers.pop_back();
tokenStack.pop_back();
i--;
}
}
return expr;
}
template <class ASTBuilder>
ASTNode finishBinaryExpression(ASTBuilder& builder, ASTNode left, ASTNode right, Scanner::SmallScannerResult* token)
{
if (token->type == Token::PunctuatorToken) {
PunctuatorKind oper = token->valuePunctuatorKind;
// Additive Operators
switch (oper) {
case Plus:
return builder.createBinaryExpressionPlusNode(left, right);
case Minus:
return builder.createBinaryExpressionMinusNode(left, right);
case LeftShift:
return builder.createBinaryExpressionLeftShiftNode(left, right);
case RightShift:
return builder.createBinaryExpressionSignedRightShiftNode(left, right);
case UnsignedRightShift:
return builder.createBinaryExpressionUnsignedRightShiftNode(left, right);
case Multiply:
return builder.createBinaryExpressionMultiplyNode(left, right);
case Divide:
return builder.createBinaryExpressionDivisionNode(left, right);
case Mod:
return builder.createBinaryExpressionModNode(left, right);
case LeftInequality:
return builder.createBinaryExpressionLessThanNode(left, right);
case RightInequality:
return builder.createBinaryExpressionGreaterThanNode(left, right);
case LeftInequalityEqual:
return builder.createBinaryExpressionLessThanOrEqualNode(left, right);
case RightInequalityEqual:
return builder.createBinaryExpressionGreaterThanOrEqualNode(left, right);
case Equal:
return builder.createBinaryExpressionEqualNode(left, right);
case NotEqual:
return builder.createBinaryExpressionNotEqualNode(left, right);
case StrictEqual:
return builder.createBinaryExpressionStrictEqualNode(left, right);
case NotStrictEqual:
return builder.createBinaryExpressionNotStrictEqualNode(left, right);
case BitwiseAnd:
return builder.createBinaryExpressionBitwiseAndNode(left, right);
case BitwiseXor:
return builder.createBinaryExpressionBitwiseXorNode(left, right);
case BitwiseOr:
return builder.createBinaryExpressionBitwiseOrNode(left, right);
case LogicalOr:
return builder.createBinaryExpressionLogicalOrNode(left, right);
case LogicalAnd:
return builder.createBinaryExpressionLogicalAndNode(left, right);
default:
RELEASE_ASSERT_NOT_REACHED();
}
} else {
ASSERT(token->type == Token::KeywordToken);
switch (token->valueKeywordKind) {
case InKeyword:
return builder.createBinaryExpressionInNode(left, right);
case KeywordKind::InstanceofKeyword:
return builder.createBinaryExpressionInstanceOfNode(left, right);
default:
RELEASE_ASSERT_NOT_REACHED();
}
}
}
// ECMA-262 12.14 Conditional Operator
template <class ASTBuilder>
ASTNode parseConditionalExpression(ASTBuilder& builder)
{
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
ASTNode exprNode = nullptr;
exprNode = this->inheritCoverGrammar(builder, &Parser::parseBinaryExpression<ASTBuilder>);
if (this->match(GuessMark)) {
ASTNode consequent = nullptr;
this->nextToken();
bool previousAllowIn = this->context->allowIn;
this->context->allowIn = true;
consequent = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
this->context->allowIn = previousAllowIn;
this->expect(Colon);
ASTNode alternate = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
exprNode = this->finalize(this->startNode(startToken), builder.createConditionalExpressionNode(exprNode, consequent, alternate));
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
}
return exprNode;
}
// ECMA-262 12.15 Assignment Operators
template <class ASTBuilder, bool checkLeftHasRestrictedWord>
ASTNode parseAssignmentExpression(ASTBuilder& builder)
{
ASTNode exprNode = nullptr;
if (!this->context->allowYield && this->matchKeyword(YieldKeyword)) {
exprNode = this->parseYieldExpression(builder);
} else {
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
ALLOC_TOKEN(token);
*token = *startToken;
ASTNodeType type;
MetaNode startNode = this->createNode();
Marker startMarker = this->lastMarker;
exprNode = this->parseConditionalExpression(builder);
type = exprNode->type();
/*
if (token.type === Token.Identifier && (token.lineNumber === this.lookahead.lineNumber) && token.value === 'async' && (this.lookahead.type === Token.Identifier)) {
const arg = this.primaryExpression<Parse>();
expr = {
type: ArrowParameterPlaceHolder,
params: [arg],
async: true
};
} */
if (type == ArrowParameterPlaceHolder || this->match(Arrow)) {
// ECMA-262 14.2 Arrow Function Definitions
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
// try to skip the parsing of arrow function
if (this->isParsingSingleFunction) {
this->scanner->index = startMarker.index;
this->scanner->lineNumber = startMarker.lineNumber;
this->scanner->lineStart = startMarker.lineStart;
this->nextToken();
InterpretedCodeBlock* currentTarget = this->codeBlock->asInterpretedCodeBlock();
size_t orgIndex = this->lookahead.start;
InterpretedCodeBlock* childBlock = currentTarget->childBlockAt(this->subCodeBlockIndex);
StringView src = childBlock->src();
this->scanner->index = src.length() + orgIndex;
this->scanner->lineNumber = childBlock->sourceElementStart().line;
this->scanner->lineStart = childBlock->sourceElementStart().index - childBlock->sourceElementStart().column;
this->lookahead.lineNumber = this->scanner->lineNumber;
this->lookahead.lineStart = this->scanner->lineStart;
this->nextToken();
// increase subCodeBlockIndex because parsing of an internal function is skipped
this->subCodeBlockIndex++;
return this->finalize(this->startNode(startToken), builder.createArrowFunctionExpressionNode(subCodeBlockIndex));
}
BEGIN_FUNCTION_SCANNING(AtomicString());
ParseFormalParametersResult list;
// FIXME reinterpretAsCoverFormalsList
if (type == Identifier) {
ASSERT(exprNode->isIdentifier());
this->validateParam(list, this->lookahead, exprNode->asIdentifier()->name());
list.params.push_back(builder.convertToParameterSyntaxNode(exprNode));
} else {
this->scanner->index = startMarker.index;
this->scanner->lineNumber = startMarker.lineNumber;
this->scanner->lineStart = startMarker.lineStart;
this->nextToken();
this->expect(LeftParenthesis);
this->currentScopeContext->m_hasArrowParameterPlaceHolder = true;
this->parseFormalParameters(newBuilder, list);
}
// validity check of ParseFormalParametersResult removed
if (this->hasLineTerminator) {
this->throwUnexpectedToken(this->lookahead);
}
this->context->firstCoverInitializedNameError.reset();
bool previousStrict = this->context->strict;
bool previousAllowYield = this->context->allowYield;
bool previousInArrowFunction = this->context->inArrowFunction;
this->context->allowYield = true;
this->context->inArrowFunction = true;
this->currentScopeContext->m_allowSuperCall = this->context->allowSuperCall;
this->currentScopeContext->m_allowSuperProperty = this->context->allowSuperProperty;
this->currentScopeContext->m_paramsStartLOC.index = startNode.index;
this->currentScopeContext->m_paramsStartLOC.column = startNode.column;
this->currentScopeContext->m_paramsStartLOC.line = startNode.line;
extractNamesFromFunctionParams(list);
this->expect(Arrow);
MetaNode node = this->startNode(startToken);
MetaNode bodyStart = this->createNode();
if (this->match(LeftBrace)) {
this->parseFunctionSourceElements(newBuilder);
} else {
LexicalBlockIndex lexicalBlockIndexBefore = this->lexicalBlockIndex;
LexicalBlockIndex lexicalBlockCountBefore = this->lexicalBlockCount;
this->lexicalBlockIndex = 0;
this->lexicalBlockCount = 0;
auto oldNameCallback = this->nameDeclaredCallback;
this->isolateCoverGrammar(newBuilder, &Parser::parseAssignmentExpression<SyntaxChecker, false>);
this->lexicalBlockIndex = lexicalBlockIndexBefore;
this->lexicalBlockCount = lexicalBlockCountBefore;
this->nameDeclaredCallback = oldNameCallback;
this->currentScopeContext->m_bodyEndLOC.index = this->lastMarker.index;
#ifndef NDEBUG
this->currentScopeContext->m_bodyEndLOC.line = this->lastMarker.lineNumber;
this->currentScopeContext->m_bodyEndLOC.column = this->lastMarker.index - this->lastMarker.lineStart;
#endif
}
this->currentScopeContext->m_lexicalBlockIndexFunctionLocatedIn = this->lexicalBlockIndex;
if (this->context->strict && list.firstRestricted) {
this->throwUnexpectedToken(list.firstRestricted, list.message);
}
if (this->context->strict && list.stricted) {
this->throwUnexpectedToken(list.stricted, list.message);
}
this->currentScopeContext->m_isArrowFunctionExpression = true;
this->currentScopeContext->m_nodeType = ASTNodeType::ArrowFunctionExpression;
this->currentScopeContext->m_isGenerator = false;
END_FUNCTION_SCANNING();
exprNode = this->finalize(node, builder.createArrowFunctionExpressionNode(subCodeBlockIndex));
this->context->strict = previousStrict;
this->context->allowYield = previousAllowYield;
this->context->inArrowFunction = previousInArrowFunction;
} else {
// check if restricted words are used as target in array/object initializer
if (checkLeftHasRestrictedWord) {
if (this->context->strict && type == Identifier) {
AtomicString name;
name = exprNode->asIdentifier()->name();
if (this->scanner->isRestrictedWord(name)) {
this->context->hasRestrictedWordInArrayOrObjectInitializer = true;
}
}
}
if (this->matchAssign()) {
if (!this->context->isAssignmentTarget) {
if (type == ArrayExpression || type == ObjectExpression) {
this->throwError(Messages::InvalidLHSInAssignment);
}
if (this->context->strict) {
this->throwError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
}
}
if (this->context->strict) {
if (type == Identifier) {
AtomicString name = exprNode->asIdentifier()->name();
if (this->scanner->isRestrictedWord(name)) {
this->throwUnexpectedToken(*token, Messages::StrictLHSAssignment);
}
if (this->scanner->isStrictModeReservedWord(name)) {
this->throwUnexpectedToken(*token, Messages::StrictReservedWord);
}
} else if (type == ArrayExpression || type == ObjectExpression) {
if (this->context->hasRestrictedWordInArrayOrObjectInitializer) {
this->throwError(Messages::StrictLHSAssignment);
}
}
}
if (!this->match(Substitution)) {
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
} else {
exprNode = builder.reinterpretExpressionAsPattern(exprNode);
if (exprNode->isLiteral() || exprNode->type() == ASTNodeType::ThisExpression) {
this->throwError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
}
}
this->nextToken(token);
ASTNode rightNode = nullptr;
ASTNode exprResult = nullptr;
rightNode = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
switch (token->valuePunctuatorKind) {
case Substitution:
exprResult = builder.createAssignmentExpressionSimpleNode(exprNode, rightNode);
break;
case PlusEqual:
exprResult = builder.createAssignmentExpressionPlusNode(exprNode, rightNode);
break;
case MinusEqual:
exprResult = builder.createAssignmentExpressionMinusNode(exprNode, rightNode);
break;
case MultiplyEqual:
exprResult = builder.createAssignmentExpressionMultiplyNode(exprNode, rightNode);
break;
case DivideEqual:
exprResult = builder.createAssignmentExpressionDivisionNode(exprNode, rightNode);
break;
case ModEqual:
exprResult = builder.createAssignmentExpressionModNode(exprNode, rightNode);
break;
case LeftShiftEqual:
exprResult = builder.createAssignmentExpressionLeftShiftNode(exprNode, rightNode);
break;
case RightShiftEqual:
exprResult = builder.createAssignmentExpressionSignedRightShiftNode(exprNode, rightNode);
break;
case UnsignedRightShiftEqual:
exprResult = builder.createAssignmentExpressionUnsignedRightShiftNode(exprNode, rightNode);
break;
case BitwiseXorEqual:
exprResult = builder.createAssignmentExpressionBitwiseXorNode(exprNode, rightNode);
break;
case BitwiseAndEqual:
exprResult = builder.createAssignmentExpressionBitwiseAndNode(exprNode, rightNode);
break;
case BitwiseOrEqual:
exprResult = builder.createAssignmentExpressionBitwiseOrNode(exprNode, rightNode);
break;
default:
RELEASE_ASSERT_NOT_REACHED();
}
exprNode = this->finalize(this->startNode(startToken), exprResult);
this->context->firstCoverInitializedNameError.reset();
}
}
}
return exprNode;
}
// ECMA-262 12.16 Comma Operator
template <class ASTBuilder>
ASTNode parseExpression(ASTBuilder& builder)
{
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
ASTNode exprNode = nullptr;
exprNode = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
if (this->match(Comma)) {
ASTNodeVector expressions;
expressions.push_back(exprNode);
while (this->startMarker.index < this->scanner->length) {
if (!this->match(Comma)) {
break;
}
this->nextToken();
expressions.push_back(this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>));
}
exprNode = this->finalize(this->startNode(startToken), builder.createSequenceExpressionNode(std::move(expressions)));
}
return exprNode;
}
// ECMA-262 13.2 Block
template <class ASTBuilder>
ASTNode parseStatementListItem(ASTBuilder& builder)
{
ASTNode statement = nullptr;
this->context->isAssignmentTarget = true;
this->context->isBindingElement = true;
this->context->hasRestrictedWordInArrayOrObjectInitializer = false;
if (this->lookahead.type == KeywordToken) {
switch (this->lookahead.valueKeywordKind) {
case FunctionKeyword:
statement = this->parseFunctionDeclaration(builder);
break;
case ExportKeyword:
if (this->sourceType != Module) {
this->throwUnexpectedToken(this->lookahead, Messages::IllegalExportDeclaration);
}
statement = this->parseExportDeclaration(builder);
break;
case ImportKeyword:
if (this->sourceType != Module) {
this->throwUnexpectedToken(this->lookahead, Messages::IllegalImportDeclaration);
}
statement = this->parseImportDeclaration(builder);
break;
case ConstKeyword:
statement = this->parseVariableStatement(builder, KeywordKind::ConstKeyword);
break;
case LetKeyword:
statement = this->isLexicalDeclaration() ? this->parseLexicalDeclaration(builder, false) : this->parseStatement(builder);
break;
case ClassKeyword:
statement = this->parseClassDeclaration(builder);
break;
default:
statement = this->parseStatement(builder);
break;
}
} else {
statement = this->parseStatement(builder);
}
return statement;
}
struct ParserBlockContext {
size_t lexicalBlockCountBefore;
size_t lexicalBlockIndexBefore;
size_t childLexicalBlockIndex;
ParserBlockContext()
: lexicalBlockCountBefore(SIZE_MAX)
, lexicalBlockIndexBefore(SIZE_MAX)
, childLexicalBlockIndex(SIZE_MAX)
{
}
};
ParserBlockContext openBlock()
{
if (UNLIKELY(this->lexicalBlockCount == LEXICAL_BLOCK_INDEX_MAX - 1)) {
this->throwError("too many lexical blocks in script", String::emptyString, String::emptyString, ErrorObject::RangeError);
}
this->lastUsingName = AtomicString();
ParserBlockContext ctx;
this->lexicalBlockCount++;
ctx.lexicalBlockCountBefore = this->lexicalBlockCount;
ctx.lexicalBlockIndexBefore = this->lexicalBlockIndex;
ctx.childLexicalBlockIndex = this->lexicalBlockCount;
this->currentScopeContext->insertBlockScope(this->allocator, ctx.childLexicalBlockIndex, this->lexicalBlockIndex,
ExtendedNodeLOC(this->lastMarker.lineNumber, this->lastMarker.index - this->lastMarker.lineStart + 1, this->lastMarker.index));
this->lexicalBlockIndex = ctx.childLexicalBlockIndex;
return ctx;
}
void closeBlock(ParserBlockContext& ctx)
{
if (this->isParsingSingleFunction) {
bool finded = false;
for (size_t i = 0; i < this->codeBlock->asInterpretedCodeBlock()->blockInfos().size(); i++) {
if (this->codeBlock->asInterpretedCodeBlock()->blockInfos()[i]->m_blockIndex == ctx.childLexicalBlockIndex) {
finded = true;
break;
}
}
if (!finded) {
ctx.childLexicalBlockIndex = LEXICAL_BLOCK_INDEX_MAX;
}
} else {
// if there is no new variable in this block, merge this block into parent block
auto currentFunctionScope = this->currentScopeContext;
auto blockContext = currentFunctionScope->findBlockFromBackward(this->lexicalBlockIndex);
if (this->lexicalBlockIndex != 0 && blockContext->m_names.size() == 0) {
const auto currentBlockIndex = this->lexicalBlockIndex;
LexicalBlockIndex parentBlockIndex = blockContext->m_parentBlockIndex;
bool isThereFunctionExists = false;
ASTFunctionScopeContext* child = currentFunctionScope->m_firstChild;
while (child) {
if (child->m_nodeType == ASTNodeType::FunctionDeclaration
&& child->m_lexicalBlockIndexFunctionLocatedIn == currentBlockIndex) {
isThereFunctionExists = true;
}
child = child->nextSibling();
}
if (!isThereFunctionExists) { // block collapse start
for (size_t i = 0; i < currentFunctionScope->m_childBlockScopes.size(); i++) {
if (currentFunctionScope->m_childBlockScopes[i]->m_parentBlockIndex == currentBlockIndex) {
currentFunctionScope->m_childBlockScopes[i]->m_parentBlockIndex = parentBlockIndex;
}
}
child = currentFunctionScope->m_firstChild;
while (child) {
if (child->m_lexicalBlockIndexFunctionLocatedIn == currentBlockIndex) {
child->m_lexicalBlockIndexFunctionLocatedIn = parentBlockIndex;
}
child = child->nextSibling();
}
auto parentBlockContext = currentFunctionScope->findBlockFromBackward(parentBlockIndex);
for (size_t i = 0; i < blockContext->m_usingNames.size(); i++) {
AtomicString name = blockContext->m_usingNames[i];
if (VectorUtil::findInVector(parentBlockContext->m_usingNames, name) == VectorUtil::invalidIndex) {
parentBlockContext->m_usingNames.push_back(name);
}
}
// remove current block context from function context
for (size_t i = 0; i < currentFunctionScope->m_childBlockScopes.size(); i++) {
if (currentFunctionScope->m_childBlockScopes[i]->m_blockIndex == currentBlockIndex) {
currentFunctionScope->m_childBlockScopes.erase(i);
break;
}
}
ctx.childLexicalBlockIndex = LEXICAL_BLOCK_INDEX_MAX;
}
}
}
this->lexicalBlockIndex = ctx.lexicalBlockIndexBefore;
this->lastUsingName = AtomicString();
}
template <class ASTBuilder>
ASTNode parseBlock(ASTBuilder& builder)
{
this->expect(LeftBrace);
ASTNode referNode = nullptr;
ParserBlockContext blockContext = openBlock();
bool allowLexicalDeclarationBefore = this->context->allowLexicalDeclaration;
this->context->allowLexicalDeclaration = true;
ASTStatementContainer block = builder.createStatementContainer();
while (true) {
if (this->match(RightBrace)) {
break;
}
referNode = block->appendChild(this->parseStatementListItem(builder), referNode);
}
this->expect(RightBrace);
this->context->allowLexicalDeclaration = allowLexicalDeclarationBefore;
closeBlock(blockContext);
MetaNode node = this->createNode();
return this->finalize(node, builder.createBlockStatementNode(block, blockContext.childLexicalBlockIndex));
}
// ECMA-262 13.3.1 Let and Const Declarations
template <class ASTBuilder>
ASTNode parseLexicalBinding(ASTBuilder& builder, KeywordKind kind, bool inFor)
{
auto node = this->createNode();
SmallScannerResultVector params;
ASTNode idNode = nullptr;
bool isIdentifier;
AtomicString name;
idNode = this->parsePattern(builder, params, kind, true);
isIdentifier = (idNode->type() == Identifier);
if (isIdentifier) {
name = idNode->asIdentifier()->name();
}
// ECMA-262 12.2.1
if (this->context->strict && isIdentifier && this->scanner->isRestrictedWord(name)) {
this->throwError(Messages::StrictVarName);
}
ASTNode init = nullptr;
if (kind == KeywordKind::ConstKeyword) {
if (!this->matchKeyword(KeywordKind::InKeyword) && !this->matchContextualKeyword("of")) {
this->expect(Substitution);
init = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
}
} else if ((!inFor && !isIdentifier) || this->match(Substitution)) {
this->expect(Substitution);
init = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
}
return this->finalize(node, builder.createVariableDeclaratorNode(kind, idNode, init));
}
template <class ASTBuilder>
ASTNodeVector parseBindingList(ASTBuilder& builder, KeywordKind kind, bool inFor)
{
ASTNodeVector list;
list.push_back(this->parseLexicalBinding(builder, kind, inFor));
while (this->match(Comma)) {
this->nextToken();
list.push_back(this->parseLexicalBinding(builder, kind, inFor));
}
return list;
}
template <class ASTBuilder>
ASTNode parseLexicalDeclaration(ASTBuilder& builder, bool inFor)
{
auto node = this->createNode();
ALLOC_TOKEN(token);
this->nextToken(token);
auto kind = token->valueKeywordKind;
ASTNodeVector declarations;
declarations = this->parseBindingList(builder, kind, inFor);
this->consumeSemicolon();
return this->finalize(node, builder.createVariableDeclarationNode(std::move(declarations), kind));
}
bool isLexicalDeclaration()
{
auto previousIndex = this->scanner->index;
auto previousLineNumber = this->scanner->lineNumber;
auto previousLineStart = this->scanner->lineStart;
this->collectComments();
ALLOC_TOKEN(next);
this->scanner->lex(next);
this->scanner->index = previousIndex;
this->scanner->lineNumber = previousLineNumber;
this->scanner->lineStart = previousLineStart;
return (next->type == Token::IdentifierToken) || (next->type == Token::PunctuatorToken && next->valuePunctuatorKind == PunctuatorKind::LeftSquareBracket) || (next->type == Token::PunctuatorToken && next->valuePunctuatorKind == PunctuatorKind::LeftBrace) || (next->type == Token::KeywordToken && next->valueKeywordKind == KeywordKind::LetKeyword) || (next->type == Token::KeywordToken && next->valueKeywordKind == KeywordKind::YieldKeyword);
}
// ECMA-262 13.3.2 Variable Statement
template <class ASTBuilder>
ASTNode parseVariableIdentifier(ASTBuilder& builder, KeywordKind kind = KeywordKindEnd, bool isExplicitVariableDeclaration = false)
{
MetaNode node = this->createNode();
ALLOC_TOKEN(token);
this->nextToken(token);
if (token->type == Token::KeywordToken && token->valueKeywordKind == YieldKeyword) {
if (this->context->strict) {
this->throwUnexpectedToken(*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->relatedSource(this->scanner->source))) {
this->throwUnexpectedToken(*token, Messages::StrictReservedWord);
} else {
if (this->context->strict || token->relatedSource(this->scanner->source) != "let" || (kind != VarKeyword)) {
this->throwUnexpectedToken(*token);
}
}
} else if (this->sourceType == Module && token->type == Token::IdentifierToken && token->relatedSource(this->scanner->source) == "await") {
this->throwUnexpectedToken(*token);
}
ASTNode id = finishIdentifier(builder, token);
if (kind == KeywordKind::VarKeyword || kind == KeywordKind::LetKeyword || kind == KeywordKind::ConstKeyword) {
addDeclaredNameIntoContext(id->asIdentifier()->name(), this->lexicalBlockIndex, kind, isExplicitVariableDeclaration);
}
return this->finalize(node, id);
}
void addDeclaredNameIntoContext(AtomicString name, LexicalBlockIndex blockIndex, KeywordKind kind, bool isExplicitVariableDeclaration = false)
{
ASSERT(kind == VarKeyword || kind == LetKeyword || kind == ConstKeyword);
if (!this->isParsingSingleFunction) {
/*
we need this bunch of code for tolerate this error(we consider variable 'e' as lexically declared)
try { } catch(e) { var e; }
*/
if (this->context->inCatchClause && isExplicitVariableDeclaration && kind == KeywordKind::VarKeyword) {
for (size_t i = 0; i < this->context->catchClauseSimplyDeclaredVariableNames.size(); i++) {
if (this->context->catchClauseSimplyDeclaredVariableNames[i] == name) {
this->currentScopeContext->insertVarName(name, blockIndex, true, kind == VarKeyword);
return;
}
}
}
/* code end */
if (!this->currentScopeContext->canDeclareName(name, blockIndex, kind == VarKeyword)) {
this->throwError(Messages::Redeclaration, new ASCIIString("Identifier"), name.string());
}
if (kind == VarKeyword) {
this->currentScopeContext->insertVarName(name, blockIndex, true, true);
} else {
this->currentScopeContext->insertNameAtBlock(name, blockIndex, kind == ConstKeyword);
this->currentScopeContext->m_needsToComputeLexicalBlockStuffs = true;
}
}
if (nameDeclaredCallback) {
nameDeclaredCallback(name, blockIndex, kind, isExplicitVariableDeclaration);
}
}
template <class ASTBuilder>
ASTNode parseVariableDeclaration(ASTBuilder& builder, DeclarationOptions& options, bool& hasInit, ASTNodeType& leftSideType)
{
SmallScannerResultVector params;
ASTNode idNode = nullptr;
bool isIdentifier;
AtomicString name;
idNode = this->parsePattern(builder, params, options.kind, true);
leftSideType = idNode->type();
isIdentifier = (leftSideType == Identifier);
if (isIdentifier) {
name = idNode->asIdentifier()->name();
}
// ECMA-262 12.2.1
if (this->context->strict && isIdentifier && this->scanner->isRestrictedWord(name)) {
this->throwError(Messages::StrictVarName);
}
if (options.kind != VarKeyword && !this->context->allowLexicalDeclaration) {
this->throwError("Lexical declaration cannot appear in a single-statement context");
}
ASTNode initNode = nullptr;
hasInit = false;
if (this->match(Substitution)) {
hasInit = true;
this->nextToken();
ASTNodeType type = ASTNodeType::ASTNodeTypeError;
initNode = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
if (initNode) {
type = initNode->type();
}
if (type == ASTNodeType::FunctionExpression || type == ASTNodeType::ArrowFunctionExpression) {
if (this->lastPoppedScopeContext->m_functionName == AtomicString()) {
this->lastPoppedScopeContext->m_functionName = name;
this->lastPoppedScopeContext->m_hasImplictFunctionName = true;
}
}
if (initNode->type() == ASTNodeType::ClassExpression) {
initNode->asClassExpression()->tryToSetImplicitName(name);
}
} else if (!isIdentifier && !options.inFor) {
this->expect(Substitution);
}
if (options.kind == ConstKeyword && !options.inFor) {
if (!initNode) {
this->throwError("Missing initializer in const identifier '%s' declaration", name.string());
}
}
MetaNode node = this->createNode();
return this->finalize(node, builder.createVariableDeclaratorNode(options.kind, idNode, initNode));
}
template <class ASTBuilder>
std::tuple<bool, bool, ASTNodeVector> parseVariableDeclarationList(ASTBuilder& builder, DeclarationOptions& options)
{
DeclarationOptions opt;
opt.inFor = options.inFor;
opt.kind = options.kind;
ASTNodeVector list;
bool hasInit;
ASTNodeType leftSideType;
list.push_back(this->parseVariableDeclaration(builder, opt, hasInit, leftSideType));
while (this->match(Comma)) {
this->nextToken();
bool ignored;
ASTNodeType ignored2;
list.push_back(this->parseVariableDeclaration(builder, opt, ignored, ignored2));
}
bool leftIsArrayOrObjectPattern = (leftSideType == ArrayPattern || leftSideType == ObjectPattern);
return std::make_tuple(hasInit, leftIsArrayOrObjectPattern, list);
}
template <class ASTBuilder>
ASTNode parseVariableStatement(ASTBuilder& builder, KeywordKind kind = VarKeyword)
{
this->expectKeyword(kind);
MetaNode node = this->createNode();
DeclarationOptions opt;
opt.inFor = false;
opt.kind = kind;
auto declarations = this->parseVariableDeclarationList(builder, opt);
ASTNodeVector declarationVector = std::get<2>(declarations);
this->consumeSemicolon();
return this->finalize(node, builder.createVariableDeclarationNode(std::move(declarationVector), kind));
}
// ECMA-262 13.4 Empty Statement
template <class ASTBuilder>
ASTNode parseEmptyStatement(ASTBuilder& builder)
{
this->expect(SemiColon);
MetaNode node = this->createNode();
return this->finalize(node, builder.createEmptyStatementNode());
}
// ECMA-262 13.5 Expression Statement
template <class ASTBuilder>
ASTNode parseExpressionStatement(ASTBuilder& builder)
{
MetaNode node = this->createNode();
ASTNode expr = this->parseExpression(builder);
this->consumeSemicolon();
return this->finalize(node, builder.createExpressionStatementNode(expr));
}
// ECMA-262 13.6 If statement
template <class ASTBuilder>
ASTNode parseIfStatement(ASTBuilder& builder)
{
ASTNode test = nullptr;
ASTNode consequent = nullptr;
ASTNode alternate = nullptr;
bool allowFunctionDeclaration = !this->context->strict;
this->expectKeyword(IfKeyword);
this->expect(LeftParenthesis);
test = this->parseExpression(builder);
this->expect(RightParenthesis);
bool allowLexicalDeclarationBefore = this->context->allowLexicalDeclaration;
this->context->allowLexicalDeclaration = false;
consequent = this->parseStatement(builder, allowFunctionDeclaration);
this->context->allowLexicalDeclaration = false;
if (this->matchKeyword(ElseKeyword)) {
this->nextToken();
alternate = this->parseStatement(builder, allowFunctionDeclaration);
}
this->context->allowLexicalDeclaration = allowLexicalDeclarationBefore;
return this->finalize(this->createNode(), builder.createIfStatementNode(test, consequent, alternate));
}
// ECMA-262 13.7.2 The do-while Statement
template <class ASTBuilder>
ASTNode parseDoWhileStatement(ASTBuilder& builder)
{
this->expectKeyword(DoKeyword);
bool previousInIteration = this->context->inIteration;
bool allowLexicalDeclarationBefore = this->context->allowLexicalDeclaration;
this->context->allowLexicalDeclaration = false;
this->context->inIteration = true;
ASTNode body = this->parseStatement(builder, false);
this->context->inIteration = previousInIteration;
this->context->allowLexicalDeclaration = allowLexicalDeclarationBefore;
this->expectKeyword(WhileKeyword);
this->expect(LeftParenthesis);
ASTNode test = this->parseExpression(builder);
this->expect(RightParenthesis);
if (this->match(SemiColon)) {
this->nextToken();
}
return this->finalize(this->createNode(), builder.createDoWhileStatementNode(test, body));
}
// ECMA-262 13.7.3 The while Statement
template <class ASTBuilder>
ASTNode parseWhileStatement(ASTBuilder& builder)
{
bool prevInLoop = this->context->inLoop;
bool allowLexicalDeclarationBefore = this->context->allowLexicalDeclaration;
this->context->allowLexicalDeclaration = false;
this->context->inLoop = true;
this->expectKeyword(WhileKeyword);
this->expect(LeftParenthesis);
ASTNode test = this->parseExpression(builder);
this->expect(RightParenthesis);
bool previousInIteration = this->context->inIteration;
this->context->inIteration = true;
ASTNode body = this->parseStatement(builder, false);
this->context->inIteration = previousInIteration;
this->context->inLoop = prevInLoop;
this->context->allowLexicalDeclaration = allowLexicalDeclarationBefore;
return this->finalize(this->createNode(), builder.createWhileStatementNode(test, body));
}
// ECMA-262 13.7.4 The for Statement
// ECMA-262 13.7.5 The for-in and for-of Statements
enum ForStatementType {
statementTypeFor,
statementTypeForIn,
statementTypeForOf
};
template <class ASTBuilder>
ASTNode parseForStatement(ASTBuilder& builder)
{
ASTNode init = nullptr;
ASTNode test = nullptr;
ASTNode update = nullptr;
ASTNode left = nullptr;
ASTNode right = nullptr;
ForStatementType type = statementTypeFor;
bool prevInLoop = this->context->inLoop;
bool isLexicalDeclaration = false;
this->expectKeyword(ForKeyword);
this->expect(LeftParenthesis);
ParserBlockContext headBlockContext = openBlock();
if (this->match(SemiColon)) {
this->nextToken();
} else {
if (this->matchKeyword(VarKeyword)) {
this->nextToken();
bool previousAllowIn = this->context->allowIn;
this->context->allowIn = false;
DeclarationOptions opt;
opt.inFor = true;
opt.kind = VarKeyword;
auto declarations = this->parseVariableDeclarationList(builder, opt);
ASTNodeVector declarationVector = std::get<2>(declarations);
this->context->allowIn = previousAllowIn;
if (declarationVector.size() == 1 && this->matchKeyword(InKeyword)) {
if (std::get<0>(declarations) && (std::get<1>(declarations) || this->context->strict)) {
this->throwError(Messages::ForInOfLoopInitializer, new ASCIIString("for-in"));
}
left = this->finalize(this->createNode(), builder.createVariableDeclarationNode(std::move(declarationVector), VarKeyword));
this->nextToken();
type = statementTypeForIn;
} else if (declarationVector.size() == 1 && !std::get<0>(declarations) && this->lookahead.type == Token::IdentifierToken && this->lookahead.relatedSource(this->scanner->source) == "of") {
left = this->finalize(this->createNode(), builder.createVariableDeclarationNode(std::move(declarationVector), VarKeyword));
this->nextToken();
type = statementTypeForOf;
} else {
init = this->finalize(this->createNode(), builder.createVariableDeclarationNode(std::move(declarationVector), VarKeyword));
this->expect(SemiColon);
}
} else if (this->matchKeyword(ConstKeyword) || this->matchKeyword(LetKeyword)) {
isLexicalDeclaration = true;
const bool previousAllowLexicalDeclaration = this->context->allowLexicalDeclaration;
this->context->allowLexicalDeclaration = true;
Scanner::ScannerResult keyword = this->lookahead;
KeywordKind kind = keyword.valueKeywordKind;
this->nextToken();
if (!this->context->strict && this->matchKeyword(InKeyword)) {
this->nextToken();
left = this->finalize(this->createNode(), builder.createIdentifierNode(AtomicString(this->escargotContext, keyword.relatedSource(this->scanner->source))));
init = nullptr;
type = statementTypeForIn;
} else {
const bool previousAllowIn = this->context->allowIn;
this->context->allowIn = false;
DeclarationOptions opt;
opt.inFor = true;
opt.kind = kind;
auto declarations = this->parseVariableDeclarationList(builder, opt);
ASTNodeVector declarationVector = std::get<2>(declarations);
this->context->allowIn = previousAllowIn;
if (declarationVector.size() == 1 && !std::get<0>(declarations) && this->matchKeyword(InKeyword)) {
left = this->finalize(this->createNode(), builder.createVariableDeclarationNode(std::move(declarationVector), kind));
this->nextToken();
type = statementTypeForIn;
} else if (declarationVector.size() == 1 && !std::get<0>(declarations) && this->lookahead.type == Token::IdentifierToken && this->lookahead.relatedSource(this->scanner->source) == "of") {
left = this->finalize(this->createNode(), builder.createVariableDeclarationNode(std::move(declarationVector), kind));
this->nextToken();
type = statementTypeForOf;
} else {
init = this->finalize(this->createNode(), builder.createVariableDeclarationNode(std::move(declarationVector), kind));
this->expect(SemiColon);
}
}
this->context->allowLexicalDeclaration = previousAllowLexicalDeclaration;
} else {
ALLOC_TOKEN(initStartToken);
*initStartToken = this->lookahead;
bool previousAllowIn = this->context->allowIn;
this->context->allowIn = false;
ASTNodeType initNodeType;
init = this->inheritCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
initNodeType = init->type();
this->context->allowIn = previousAllowIn;
if (this->matchKeyword(InKeyword)) {
if (initNodeType == ASTNodeType::Literal || (initNodeType >= ASTNodeType::AssignmentExpression && initNodeType <= ASTNodeType::AssignmentExpressionSimple) || initNodeType == ASTNodeType::ThisExpression) {
this->throwError(Messages::InvalidLHSInForIn);
}
this->nextToken();
init = builder.reinterpretExpressionAsPattern(init);
left = init;
init = nullptr;
type = statementTypeForIn;
} else if (this->lookahead.type == Token::IdentifierToken && this->lookahead.relatedSource(this->scanner->source) == "of") {
if (!this->context->isAssignmentTarget || (initNodeType >= ASTNodeType::AssignmentExpression && initNodeType <= ASTNodeType::AssignmentExpressionSimple)) {
this->throwError(Messages::InvalidLHSInForLoop);
}
this->nextToken();
init = builder.reinterpretExpressionAsPattern(init);
left = init;
init = nullptr;
type = statementTypeForOf;
} else {
if (this->match(Comma)) {
ASTNodeVector initSeq;
initSeq.push_back(init);
while (this->match(Comma)) {
this->nextToken();
initSeq.push_back(this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>));
}
init = this->finalize(this->startNode(initStartToken), builder.createSequenceExpressionNode(std::move(initSeq)));
}
this->expect(SemiColon);
}
}
}
ParserBlockContext iterationBlockContext;
if (type == statementTypeFor) {
iterationBlockContext = openBlock();
this->context->inLoop = true;
if (!this->match(SemiColon)) {
test = this->parseExpression(builder);
}
this->expect(SemiColon);
if (!this->match(RightParenthesis)) {
update = this->parseExpression(builder);
}
} else if (type == statementTypeForIn) {
ASSERT(left);
right = this->parseExpression(builder);
iterationBlockContext = openBlock();
} else {
ASSERT(type == statementTypeForOf);
ASSERT(left);
right = this->parseAssignmentExpression<ASTBuilder, false>(builder);
iterationBlockContext = openBlock();
}
this->expect(RightParenthesis);
ASTBlockScopeContext* headBlockContextInstance = this->currentScopeContext->findBlockFromBackward(headBlockContext.childLexicalBlockIndex);
if (headBlockContextInstance->m_names.size()) {
// if there are names on headContext (let, const)
// we should copy names into to iterationBlock
this->currentScopeContext->findBlockFromBackward(iterationBlockContext.childLexicalBlockIndex)->m_names = headBlockContextInstance->m_names;
}
bool previousInIteration = this->context->inIteration;
bool allowLexicalDeclarationBefore = this->context->allowLexicalDeclaration;
this->context->allowLexicalDeclaration = false;
this->context->inIteration = true;
ASTNode body = nullptr;
auto functor = std::bind(&Parser::parseStatement<ASTBuilder>, std::ref(*this), builder, false);
body = this->isolateCoverGrammarWithFunctor(builder, functor);
this->context->inIteration = previousInIteration;
this->context->inLoop = prevInLoop;
this->context->allowLexicalDeclaration = allowLexicalDeclarationBefore;
closeBlock(iterationBlockContext);
closeBlock(headBlockContext);
MetaNode node = this->createNode();
if (type == statementTypeFor) {
ASTNode forNode = builder.createForStatementNode(init, test, update, body, isLexicalDeclaration, headBlockContext.childLexicalBlockIndex, iterationBlockContext.childLexicalBlockIndex);
return this->finalize(node, forNode);
}
if (type == statementTypeForIn) {
ASTNode forInNode = builder.createForInOfStatementNode(left, right, body, true, isLexicalDeclaration, headBlockContext.childLexicalBlockIndex, iterationBlockContext.childLexicalBlockIndex);
return this->finalize(node, forInNode);
}
ASSERT(type == statementTypeForOf);
ASTNode forOfNode = builder.createForInOfStatementNode(left, right, body, false, isLexicalDeclaration, headBlockContext.childLexicalBlockIndex, iterationBlockContext.childLexicalBlockIndex);
return this->finalize(node, forOfNode);
}
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
template <class ASTBuilder>
ASTNode parseContinueStatement(ASTBuilder& builder)
{
this->expectKeyword(ContinueKeyword);
AtomicString labelString;
if (this->lookahead.type == IdentifierToken && !this->hasLineTerminator) {
ASTNode labelNode = this->parseVariableIdentifier(builder);
labelString = labelNode->asIdentifier()->name();
if (!hasLabel(labelString)) {
this->throwError(Messages::UnknownLabel, labelString.string());
}
for (size_t i = 0; i < this->context->labelSet.size(); i++) {
if (this->context->labelSet[i].first == labelString && !this->context->labelSet[i].second) {
this->throwError(Messages::UnknownLabel, labelString.string());
}
}
} else if (!this->context->inIteration) {
this->throwError(Messages::IllegalContinue);
}
this->consumeSemicolon();
MetaNode node = this->createNode();
if (labelString.string()->length() != 0) {
return this->finalize(node, builder.createContinueLabelStatementNode(labelString.string()));
} else {
return this->finalize(node, builder.createContinueStatementNode());
}
}
// ECMA-262 13.9 The break statement
template <class ASTBuilder>
ASTNode parseBreakStatement(ASTBuilder& builder)
{
this->expectKeyword(BreakKeyword);
AtomicString labelString;
if (this->lookahead.type == IdentifierToken && !this->hasLineTerminator) {
ASTNode labelNode = this->parseVariableIdentifier(builder);
labelString = labelNode->asIdentifier()->name();
if (!hasLabel(labelString)) {
this->throwError(Messages::UnknownLabel, labelString.string());
}
} else if (!this->context->inIteration && !this->context->inSwitch) {
this->throwError(Messages::IllegalBreak);
}
this->consumeSemicolon();
MetaNode node = this->createNode();
if (labelString.string()->length() != 0) {
return this->finalize(node, builder.createBreakLabelStatementNode(labelString.string()));
} else {
return this->finalize(node, builder.createBreakStatementNode());
}
}
// ECMA-262 13.10 The return statement
template <class ASTBuilder>
ASTNode parseReturnStatement(ASTBuilder& builder)
{
if (!this->context->inFunctionBody) {
this->throwError(Messages::IllegalReturn);
}
this->expectKeyword(ReturnKeyword);
bool hasArgument = !this->match(SemiColon) && !this->match(RightBrace) && !this->hasLineTerminator && this->lookahead.type != EOFToken;
ASTNode argument = nullptr;
if (hasArgument) {
argument = this->parseExpression(builder);
}
this->consumeSemicolon();
return this->finalize(this->createNode(), builder.createReturnStatementNode(argument));
}
// ECMA-262 13.11 The with statement
template <class ASTBuilder>
ASTNode parseWithStatement(ASTBuilder& builder)
{
if (this->context->strict) {
this->throwError(Messages::StrictModeWith);
}
this->expectKeyword(WithKeyword);
MetaNode node = this->createNode();
this->expect(LeftParenthesis);
ASTNode object = this->parseExpression(builder);
this->expect(RightParenthesis);
this->currentScopeContext->m_hasWith = true;
bool prevInWith = this->context->inWith;
this->context->inWith = true;
ASTNode body = this->parseStatement(builder, false);
this->context->inWith = prevInWith;
return this->finalize(node, builder.createWithStatementNode(object, body));
}
// ECMA-262 13.12 The switch statement
template <class ASTBuilder>
ASTNode parseSwitchCase(ASTBuilder& builder, bool& isDefaultNode)
{
MetaNode node = this->createNode();
ASTNode test = nullptr;
if (this->matchKeyword(DefaultKeyword)) {
node = this->createNode();
this->nextToken();
isDefaultNode = true;
} else {
this->expectKeyword(CaseKeyword);
node = this->createNode();
test = this->parseExpression(builder);
isDefaultNode = false;
}
this->expect(Colon);
ASTStatementContainer consequent = builder.createStatementContainer();
while (true) {
if (this->match(RightBrace) || this->matchKeyword(DefaultKeyword) || this->matchKeyword(CaseKeyword)) {
break;
}
consequent->appendChild(this->parseStatementListItem(builder));
}
return this->finalize(node, builder.createSwitchCaseNode(test, consequent));
}
template <class ASTBuilder>
ASTNode parseSwitchStatement(ASTBuilder& builder)
{
this->expectKeyword(SwitchKeyword);
this->expect(LeftParenthesis);
ASTNode discriminant = this->parseExpression(builder);
this->expect(RightParenthesis);
bool previousInSwitch = this->context->inSwitch;
this->context->inSwitch = true;
ASTStatementContainer casesA, casesB;
ASTNode deflt = nullptr;
casesA = builder.createStatementContainer();
casesB = builder.createStatementContainer();
bool isDefaultNode = false;
bool defaultFound = false;
this->expect(LeftBrace);
while (true) {
if (this->match(RightBrace)) {
break;
}
ASTNode clause = this->parseSwitchCase(builder, isDefaultNode);
if (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(this->createNode(), builder.createSwitchStatementNode(discriminant, casesA, deflt, casesB));
}
// ECMA-262 13.13 Labelled Statements
template <class ASTBuilder>
ASTNode parseLabelledStatement(ASTBuilder& builder, size_t multiLabelCount = 1)
{
ASTNode expr = nullptr;
AtomicString name;
bool isIdentifier = false;
expr = this->parseExpression(builder);
if (expr->type() == Identifier) {
isIdentifier = true;
name = expr->asIdentifier()->name();
}
ASTNode statement = nullptr;
if (isIdentifier && this->match(Colon)) {
this->nextToken();
if (hasLabel(name)) {
this->throwError(Messages::Redeclaration, new ASCIIString("Label"), name.string());
}
this->context->labelSet.push_back(std::make_pair(name, false));
if (this->lookahead.type == IdentifierToken) {
ASTNode labeledBody = this->parseLabelledStatement(builder, multiLabelCount + 1);
statement = builder.createLabeledStatementNode(labeledBody, name.string());
} else {
if (this->matchKeyword(DoKeyword) || this->matchKeyword(ForKeyword) || this->matchKeyword(WhileKeyword)) {
// Turn labels to accept continue references.
size_t end = this->context->labelSet.size() - 1;
for (size_t i = 0; i < multiLabelCount; i++) {
ASSERT(end >= i);
this->context->labelSet[end - i].second = true;
}
}
ASTNode labeledBody = this->parseStatement(builder, !this->context->strict);
statement = builder.createLabeledStatementNode(labeledBody, name.string());
}
removeLabel(name);
} else {
this->consumeSemicolon();
statement = builder.createExpressionStatementNode(expr);
}
return this->finalize(this->createNode(), statement);
}
// ECMA-262 13.14 The throw statement
template <class ASTBuilder>
ASTNode parseThrowStatement(ASTBuilder& builder)
{
auto metaNode = this->createNode();
this->expectKeyword(ThrowKeyword);
if (this->hasLineTerminator) {
this->throwError(Messages::NewlineAfterThrow);
}
ASTNode argument = this->parseExpression(builder);
this->consumeSemicolon();
return this->finalize(metaNode, builder.createThrowStatementNode(argument));
}
// ECMA-262 13.15 The try statement
template <class ASTBuilder>
ASTNode parseCatchClause(ASTBuilder& builder)
{
this->expectKeyword(CatchKeyword);
this->expect(LeftParenthesis);
if (this->match(RightParenthesis)) {
this->throwUnexpectedToken(this->lookahead);
}
ParserBlockContext catchBlockContext = openBlock();
SmallScannerResultVector params;
ASTNode param = this->parsePattern(builder, params, KeywordKind::LetKeyword);
if (this->context->strict && param->type() == Identifier) {
if (this->scanner->isRestrictedWord(param->asIdentifier()->name())) {
this->throwError(Messages::StrictCatchVariable);
}
}
this->expect(RightParenthesis);
bool oldInCatchClause = this->context->inCatchClause;
this->context->inCatchClause = true;
bool gotSimplyDeclaredVariableName = param->isIdentifier();
if (gotSimplyDeclaredVariableName) {
this->context->catchClauseSimplyDeclaredVariableNames.push_back(param->asIdentifier()->name());
}
ASTNode body = this->parseBlock(builder);
if (gotSimplyDeclaredVariableName) {
this->context->catchClauseSimplyDeclaredVariableNames.pop_back();
}
this->context->inCatchClause = oldInCatchClause;
closeBlock(catchBlockContext);
return this->finalize(this->createNode(), builder.createCatchClauseNode(param, nullptr, body, catchBlockContext.childLexicalBlockIndex));
}
template <class ASTBuilder>
ASTNode parseFinallyClause(ASTBuilder& builder)
{
this->expectKeyword(FinallyKeyword);
return this->parseBlock(builder);
}
template <class ASTBuilder>
ASTNode parseTryStatement(ASTBuilder& builder)
{
this->expectKeyword(TryKeyword);
ASTNode block = this->parseBlock(builder);
ASTNode handler = nullptr;
if (this->matchKeyword(CatchKeyword)) {
handler = this->parseCatchClause(builder);
}
ASTNode finalizer = nullptr;
if (this->matchKeyword(FinallyKeyword)) {
finalizer = this->parseFinallyClause(builder);
}
if (!handler && !finalizer) {
this->throwError(Messages::NoCatchOrFinally);
}
return this->finalize(this->createNode(), builder.createTryStatementNode(block, handler, finalizer));
}
// ECMA-262 13.16 The debugger statement
template <class ASTBuilder>
ASTNode parseDebuggerStatement(ASTBuilder& builder)
{
ESCARGOT_LOG_ERROR("debugger keyword is not supported yet");
MetaNode node = this->createNode();
this->expectKeyword(KeywordKind::DebuggerKeyword);
this->consumeSemicolon();
return this->finalize(node, builder.createDebuggerStatementNode());
}
// ECMA-262 13 Statements
template <class ASTBuilder>
ASTNode parseStatement(ASTBuilder& builder, bool allowFunctionDeclaration = true)
{
checkRecursiveLimit();
ASTNode statement = nullptr;
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(builder);
break;
case Token::PunctuatorToken: {
PunctuatorKind value = this->lookahead.valuePunctuatorKind;
if (value == LeftBrace) {
statement = this->parseBlock(builder);
} else if (value == LeftParenthesis) {
statement = this->parseExpressionStatement(builder);
} else if (value == SemiColon) {
statement = this->parseEmptyStatement(builder);
} else {
statement = this->parseExpressionStatement(builder);
}
break;
}
case Token::IdentifierToken:
statement = this->parseLabelledStatement(builder);
break;
case Token::KeywordToken:
switch (this->lookahead.valueKeywordKind) {
case BreakKeyword:
statement = this->parseBreakStatement(builder);
break;
case ContinueKeyword:
statement = this->parseContinueStatement(builder);
break;
case DebuggerKeyword:
statement = this->parseDebuggerStatement(builder);
break;
case DoKeyword:
statement = this->parseDoWhileStatement(builder);
break;
case ForKeyword:
statement = this->parseForStatement(builder);
break;
case FunctionKeyword: {
if (!allowFunctionDeclaration) {
this->throwUnexpectedToken(this->lookahead);
}
statement = this->parseFunctionDeclaration(builder);
break;
}
case IfKeyword:
statement = this->parseIfStatement(builder);
break;
case ReturnKeyword:
statement = this->parseReturnStatement(builder);
break;
case SwitchKeyword:
statement = this->parseSwitchStatement(builder);
break;
case ThrowKeyword:
statement = this->parseThrowStatement(builder);
break;
case TryKeyword:
statement = this->parseTryStatement(builder);
break;
// Lexical declaration (let, const) cannot appear in a single-statement context
case VarKeyword:
statement = this->parseVariableStatement(builder, this->lookahead.valueKeywordKind);
break;
case WhileKeyword:
statement = this->parseWhileStatement(builder);
break;
case WithKeyword:
statement = this->parseWithStatement(builder);
break;
case ClassKeyword:
statement = this->parseClassDeclaration(builder);
break;
case YieldKeyword: {
// TODO consider case that class method is generator function
if (builder.isNodeGenerator()) {
if (this->context->strict) {
this->throwError("Cannot use yield as a label in strict mode");
}
statement = this->parseLabelledStatement(builder);
} else {
statement = this->parseExpressionStatement(builder);
}
break;
}
default:
statement = this->parseExpressionStatement(builder);
break;
}
break;
default:
this->throwUnexpectedToken(this->lookahead);
}
return statement;
}
StatementContainer* parseFunctionParameters(NodeGenerator& builder)
{
StatementContainer* container = builder.createStatementContainer();
this->expect(LeftParenthesis);
ParseFormalParametersResult options;
bool end = false;
size_t paramIndex = 0;
MetaNode node = this->createNode();
while (!this->match(RightParenthesis)) {
Node* param = this->parseFormalParameter(builder, options, end);
switch (param->type()) {
case Identifier:
case AssignmentPattern:
case ArrayPattern:
case ObjectPattern: {
InitializeParameterExpressionNode* init = this->finalize(node, builder.createInitializeParameterExpressionNode(param, paramIndex));
Node* statement = this->finalize(node, builder.createExpressionStatementNode(init));
container->appendChild(statement);
break;
}
case RestElement: {
Node* statement = this->finalize(node, builder.createExpressionStatementNode(param));
container->appendChild(statement);
break;
}
default: {
ASSERT_NOT_REACHED();
break;
}
}
if (end) {
break;
}
paramIndex++;
this->expect(Comma);
}
this->expect(RightParenthesis);
return container;
}
BlockStatementNode* parseFunctionBody(NodeGenerator& builder)
{
// only for parsing the body of callee function
ASSERT(this->isParsingSingleFunction);
this->lexicalBlockIndex = 0;
this->lexicalBlockCount = 0;
this->context->inCatchClause = false;
MetaNode nodeStart = this->createNode();
StatementContainer* body = builder.createStatementContainer();
this->expect(LeftBrace);
this->parseDirectivePrologues(builder, body);
this->context->labelSet.clear();
this->context->inIteration = false;
this->context->inSwitch = false;
this->context->inFunctionBody = true;
Node* referNode = nullptr;
while (this->startMarker.index < this->scanner->length) {
if (this->match(RightBrace)) {
break;
}
referNode = body->appendChild(this->parseStatementListItem(builder), referNode);
}
bool isEndedWithReturnNode = referNode && referNode->type() == ASTNodeType::ReturnStatement;
if (!isEndedWithReturnNode) {
referNode = body->appendChild(builder.createReturnStatementNode(nullptr));
}
this->expect(RightBrace);
return this->finalize(nodeStart, builder.createBlockStatementNode(body, 0));
}
// ECMA-262 14.1 Function Definition
template <class ASTBuilder>
ASTNode parseFunctionSourceElements(ASTBuilder& builder)
{
// parseFunctionSourceElements only scans the body of function
// should be invoked only during the global parsing (program)
ASSERT(!builder.isNodeGenerator() && !this->isParsingSingleFunction);
// return empty node because the function body node is never used now
ASTNode result = nullptr;
LexicalBlockIndex lexicalBlockIndexBefore = this->lexicalBlockIndex;
LexicalBlockIndex lexicalBlockCountBefore = this->lexicalBlockCount;
auto oldNameCallback = this->nameDeclaredCallback;
this->lexicalBlockIndex = 0;
this->lexicalBlockCount = 0;
bool oldInCatchClause = this->context->inCatchClause;
this->context->inCatchClause = false;
auto oldCatchClauseSimplyDeclaredVariableNames = std::move(this->context->catchClauseSimplyDeclaredVariableNames);
MetaNode nodeStart = this->createNode();
ASTStatementContainer body = builder.createStatementContainer();
this->expect(LeftBrace);
this->parseDirectivePrologues(builder, body);
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;
while (this->startMarker.index < this->scanner->length) {
if (this->match(RightBrace)) {
break;
}
this->parseStatementListItem(builder);
}
this->expect(RightBrace);
this->context->inCatchClause = oldInCatchClause;
this->context->catchClauseSimplyDeclaredVariableNames = std::move(oldCatchClauseSimplyDeclaredVariableNames);
this->lexicalBlockIndex = lexicalBlockIndexBefore;
this->lexicalBlockCount = lexicalBlockCountBefore;
this->nameDeclaredCallback = oldNameCallback;
this->context->labelSet = previousLabelSet;
this->context->inIteration = previousInIteration;
this->context->inSwitch = previousInSwitch;
this->context->inFunctionBody = previousInFunctionBody;
this->currentScopeContext->m_lexicalBlockIndexFunctionLocatedIn = lexicalBlockIndexBefore;
this->currentScopeContext->m_bodyEndLOC.index = this->lastMarker.index;
#ifndef NDEBUG
this->currentScopeContext->m_bodyEndLOC.line = this->lastMarker.lineNumber;
this->currentScopeContext->m_bodyEndLOC.column = this->lastMarker.index - this->lastMarker.lineStart;
#endif
//return this->finalize(nodeStart, builder.createBlockStatementNode(StatementContainer::create(), this->lexicalBlockIndex));
return result;
}
template <class ASTBuilder>
ASTNode parseFunctionDeclaration(ASTBuilder& builder)
{
MetaNode node = this->createNode();
this->expectKeyword(FunctionKeyword);
bool isGenerator = this->match(Multiply);
if (isGenerator) {
this->nextToken();
}
const char* message = nullptr;
ASTNode id = nullptr;
Scanner::SmallScannerResult firstRestricted;
{
ALLOC_TOKEN(token);
*token = this->lookahead;
id = this->parseVariableIdentifier(builder);
if (this->context->strict) {
if (this->scanner->isRestrictedWord(token->relatedSource(this->scanner->source))) {
this->throwUnexpectedToken(*token, Messages::StrictFunctionName);
}
} else {
if (this->scanner->isRestrictedWord(token->relatedSource(this->scanner->source))) {
firstRestricted = *token;
message = Messages::StrictFunctionName;
} else if (this->scanner->isStrictModeReservedWord(token->relatedSource(this->scanner->source))) {
firstRestricted = *token;
message = Messages::StrictReservedWord;
}
}
}
ASSERT(id);
AtomicString fnName = id->asIdentifier()->name();
if (tryToSkipFunctionParsing()) {
return this->finalize(node, builder.createFunctionDeclarationNode(isGenerator, subCodeBlockIndex, fnName));
}
MetaNode paramsStart = this->createNode();
this->expect(LeftParenthesis);
addDeclaredNameIntoContext(fnName, this->lexicalBlockIndex, KeywordKind::VarKeyword);
insertUsingName(fnName);
BEGIN_FUNCTION_SCANNING(fnName);
this->currentScopeContext->m_paramsStartLOC.index = paramsStart.index;
this->currentScopeContext->m_paramsStartLOC.column = paramsStart.column;
this->currentScopeContext->m_paramsStartLOC.line = paramsStart.line;
bool previousAllowYield = this->context->allowYield;
bool previousInArrowFunction = this->context->inArrowFunction;
this->context->allowYield = !isGenerator;
this->context->inArrowFunction = false;
ParseFormalParametersResult formalParameters;
this->parseFormalParameters(newBuilder, formalParameters, &firstRestricted);
Scanner::SmallScannerResult stricted = formalParameters.stricted;
firstRestricted = formalParameters.firstRestricted;
if (formalParameters.message) {
message = formalParameters.message;
}
extractNamesFromFunctionParams(formalParameters);
bool previousStrict = this->context->strict;
this->parseFunctionSourceElements(newBuilder);
if (this->context->strict && firstRestricted) {
this->throwUnexpectedToken(firstRestricted, message);
}
if (this->context->strict && stricted) {
this->throwUnexpectedToken(stricted, message);
}
this->context->strict = previousStrict;
this->context->allowYield = previousAllowYield;
this->context->inArrowFunction = previousInArrowFunction;
this->currentScopeContext->m_nodeType = ASTNodeType::FunctionDeclaration;
this->currentScopeContext->m_isGenerator = isGenerator;
END_FUNCTION_SCANNING();
return this->finalize(node, builder.createFunctionDeclarationNode(isGenerator, subCodeBlockIndex, fnName));
}
template <class ASTBuilder>
ASTNode parseFunctionExpression(ASTBuilder& builder)
{
MetaNode node = this->createNode();
this->expectKeyword(FunctionKeyword);
bool isGenerator = this->match(Multiply);
if (isGenerator) {
this->nextToken();
}
const char* message = nullptr;
ASTNode id = nullptr;
Scanner::SmallScannerResult firstRestricted;
bool previousAllowYield = this->context->allowYield;
bool previousInArrowFunction = this->context->inArrowFunction;
this->context->allowYield = !isGenerator;
this->context->inArrowFunction = false;
if (!this->match(LeftParenthesis)) {
ALLOC_TOKEN(token);
*token = this->lookahead;
id = (!this->context->strict && !isGenerator && this->matchKeyword(YieldKeyword)) ? this->parseIdentifierName(builder) : this->parseVariableIdentifier(builder);
if (this->context->strict) {
if (this->scanner->isRestrictedWord(token->relatedSource(this->scanner->source))) {
this->throwUnexpectedToken(*token, Messages::StrictFunctionName);
}
} else {
if (this->scanner->isRestrictedWord(token->relatedSource(this->scanner->source))) {
firstRestricted = *token;
message = Messages::StrictFunctionName;
} else if (this->scanner->isStrictModeReservedWord(token->relatedSource(this->scanner->source))) {
firstRestricted = *token;
message = Messages::StrictReservedWord;
}
}
}
AtomicString fnName = id ? id->asIdentifier()->name() : AtomicString();
if (tryToSkipFunctionParsing()) {
this->context->allowYield = previousAllowYield;
this->context->inArrowFunction = previousInArrowFunction;
return this->finalize(node, builder.createFunctionExpressionNode(isGenerator, subCodeBlockIndex, fnName));
}
MetaNode paramsStart = this->createNode();
this->expect(LeftParenthesis);
BEGIN_FUNCTION_SCANNING(fnName);
if (id) {
this->currentScopeContext->insertVarName(fnName, 0, false);
this->currentScopeContext->insertUsingName(fnName, 0);
}
this->currentScopeContext->m_paramsStartLOC.index = paramsStart.index;
this->currentScopeContext->m_paramsStartLOC.column = paramsStart.column;
this->currentScopeContext->m_paramsStartLOC.line = paramsStart.line;
ParseFormalParametersResult formalParameters;
this->parseFormalParameters(newBuilder, formalParameters, &firstRestricted);
Scanner::SmallScannerResult stricted = formalParameters.stricted;
firstRestricted = formalParameters.firstRestricted;
if (formalParameters.message) {
message = formalParameters.message;
}
extractNamesFromFunctionParams(formalParameters);
bool previousStrict = this->context->strict;
this->parseFunctionSourceElements(newBuilder);
if (this->context->strict && firstRestricted) {
this->throwUnexpectedToken(firstRestricted, message);
}
if (this->context->strict && stricted) {
this->throwUnexpectedToken(stricted, message);
}
this->context->strict = previousStrict;
this->context->allowYield = previousAllowYield;
this->context->inArrowFunction = previousInArrowFunction;
this->currentScopeContext->m_nodeType = ASTNodeType::FunctionExpression;
this->currentScopeContext->m_isGenerator = isGenerator;
END_FUNCTION_SCANNING();
return this->finalize(node, builder.createFunctionExpressionNode(isGenerator, subCodeBlockIndex, fnName));
}
// ECMA-262 14.1.1 Directive Prologues
template <class ASTBuilder>
ASTNode parseDirective(ASTBuilder& builder, bool& useStrict)
{
ALLOC_TOKEN(token);
*token = this->lookahead;
bool isLiteral = false;
useStrict = false;
MetaNode node = this->createNode();
ASTNode expr = this->parseExpression(builder);
if (expr->type() == Literal) {
isLiteral = true;
if (token->valueStringLiteral(this->scanner).equals("use strict")) {
useStrict = true;
}
}
this->consumeSemicolon();
if (isLiteral) {
return this->finalize(node, builder.createDirectiveNode(expr));
} else {
return this->finalize(node, builder.createExpressionStatementNode(expr));
}
}
template <class ASTBuilder>
void parseDirectivePrologues(ASTBuilder& builder, ASTStatementContainer container)
{
ASSERT(container);
bool useStrict = false;
Scanner::SmallScannerResult firstRestricted;
ALLOC_TOKEN(token);
while (true) {
*token = this->lookahead;
if (token->type != StringLiteralToken) {
break;
}
ASTNode statement = this->parseDirective(builder, useStrict);
container->appendChild(statement);
if (statement->type() != Directive) {
break;
}
if (!token->hasAllocatedString && useStrict) {
this->currentScopeContext->m_isStrict = this->context->strict = true;
if (firstRestricted) {
this->throwUnexpectedToken(firstRestricted, Messages::StrictOctalLiteral);
}
} else {
if (!firstRestricted && token->octal) {
firstRestricted = *token;
}
}
}
}
// ECMA-262 14.3 Method Definitions
template <class ASTBuilder>
ASTNode parseGetterMethod(ASTBuilder& builder)
{
MetaNode node = this->createNode();
bool isGenerator = false;
if (tryToSkipFunctionParsing()) {
return this->finalize(node, builder.createFunctionExpressionNode(isGenerator, this->subCodeBlockIndex, AtomicString()));
}
const bool previousAllowYield = this->context->allowYield;
const bool previousInArrowFunction = this->context->inArrowFunction;
const bool previousAllowSuperProperty = this->context->allowSuperProperty;
this->context->allowYield = true;
this->context->inArrowFunction = false;
this->context->allowSuperProperty = true;
this->expect(LeftParenthesis);
this->expect(RightParenthesis);
BEGIN_FUNCTION_SCANNING(AtomicString());
ParseFormalParametersResult params;
extractNamesFromFunctionParams(params);
this->parsePropertyMethod(newBuilder, params);
this->context->allowYield = previousAllowYield;
this->context->inArrowFunction = previousInArrowFunction;
this->context->allowSuperProperty = previousAllowSuperProperty;
this->currentScopeContext->m_paramsStartLOC.index = node.index;
this->currentScopeContext->m_paramsStartLOC.column = node.column;
this->currentScopeContext->m_paramsStartLOC.line = node.line;
this->currentScopeContext->m_allowSuperProperty = true;
this->currentScopeContext->m_nodeType = ASTNodeType::FunctionExpression;
this->currentScopeContext->m_isGenerator = isGenerator;
END_FUNCTION_SCANNING();
return this->finalize(node, builder.createFunctionExpressionNode(isGenerator, this->subCodeBlockIndex, AtomicString()));
}
template <class ASTBuilder>
ASTNode parseSetterMethod(ASTBuilder& builder)
{
MetaNode node = this->createNode();
bool isGenerator = false;
if (tryToSkipFunctionParsing()) {
return this->finalize(node, builder.createFunctionExpressionNode(isGenerator, this->subCodeBlockIndex, AtomicString()));
}
const bool previousAllowYield = this->context->allowYield;
const bool previousInArrowFunction = this->context->inArrowFunction;
const bool previousAllowSuperProperty = this->context->allowSuperProperty;
this->context->allowYield = true;
this->context->allowSuperProperty = true;
this->context->inArrowFunction = false;
this->expect(LeftParenthesis);
BEGIN_FUNCTION_SCANNING(AtomicString());
ParseFormalParametersResult formalParameters;
this->parseFormalParameters(newBuilder, formalParameters);
if (formalParameters.params.size() != 1) {
this->throwError(Messages::BadSetterArity);
} else if (formalParameters.params[0]->type() == ASTNodeType::RestElement) {
this->throwError(Messages::BadSetterRestParameter);
}
extractNamesFromFunctionParams(formalParameters);
this->parsePropertyMethod(newBuilder, formalParameters);
this->context->allowYield = previousAllowYield;
this->context->allowSuperProperty = previousAllowSuperProperty;
this->context->inArrowFunction = previousInArrowFunction;
this->currentScopeContext->m_paramsStartLOC.index = node.index;
this->currentScopeContext->m_paramsStartLOC.column = node.column;
this->currentScopeContext->m_paramsStartLOC.line = node.line;
this->currentScopeContext->m_allowSuperProperty = true;
this->currentScopeContext->m_nodeType = ASTNodeType::FunctionExpression;
this->currentScopeContext->m_isGenerator = isGenerator;
END_FUNCTION_SCANNING();
return this->finalize(node, builder.createFunctionExpressionNode(isGenerator, this->subCodeBlockIndex, AtomicString()));
}
template <class ASTBuilder>
ASTNode parseGeneratorMethod(ASTBuilder& builder)
{
MetaNode node = this->createNode();
bool isGenerator = true;
if (tryToSkipFunctionParsing()) {
return this->finalize(node, builder.createFunctionExpressionNode(isGenerator, this->subCodeBlockIndex, AtomicString()));
}
const bool previousAllowYield = this->context->allowYield;
const bool previousInArrowFunction = this->context->inArrowFunction;
const bool previousAllowSuperProperty = this->context->allowSuperProperty;
this->context->allowYield = false;
this->context->allowSuperProperty = true;
this->context->inArrowFunction = false;
this->expect(LeftParenthesis);
BEGIN_FUNCTION_SCANNING(AtomicString());
ParseFormalParametersResult formalParameters;
this->parseFormalParameters(newBuilder, formalParameters);
extractNamesFromFunctionParams(formalParameters);
this->parsePropertyMethod(newBuilder, formalParameters);
this->context->allowYield = previousAllowYield;
this->context->allowSuperProperty = previousAllowSuperProperty;
this->context->inArrowFunction = previousInArrowFunction;
this->currentScopeContext->m_paramsStartLOC.index = node.index;
this->currentScopeContext->m_paramsStartLOC.column = node.column;
this->currentScopeContext->m_paramsStartLOC.line = node.line;
this->currentScopeContext->m_allowSuperProperty = true;
this->currentScopeContext->m_nodeType = ASTNodeType::FunctionExpression;
this->currentScopeContext->m_isGenerator = isGenerator;
END_FUNCTION_SCANNING();
return this->finalize(node, builder.createFunctionExpressionNode(isGenerator, this->subCodeBlockIndex, AtomicString()));
}
// ECMA-262 14.4 Generator Function Definitions
bool isStartOfExpression()
{
bool start = true;
if (this->lookahead.type == Token::PunctuatorToken) {
switch (this->lookahead.valuePunctuatorKind) {
case PunctuatorKind::LeftSquareBracket:
case PunctuatorKind::LeftParenthesis:
case PunctuatorKind::LeftBrace:
case PunctuatorKind::Plus:
case PunctuatorKind::Minus:
case PunctuatorKind::ExclamationMark:
case PunctuatorKind::Wave:
case PunctuatorKind::PlusPlus:
case PunctuatorKind::MinusMinus:
case PunctuatorKind::Divide:
case PunctuatorKind::DivideEqual:
start = true;
break;
default:
start = false;
break;
}
} else if (this->lookahead.type == Token::KeywordToken) {
switch (this->lookahead.valueKeywordKind) {
case KeywordKind::ClassKeyword:
case KeywordKind::DeleteKeyword:
case KeywordKind::FunctionKeyword:
case KeywordKind::LetKeyword:
case KeywordKind::NewKeyword:
case KeywordKind::SuperKeyword:
case KeywordKind::ThisKeyword:
case KeywordKind::TypeofKeyword:
case KeywordKind::VoidKeyword:
case KeywordKind::YieldKeyword:
start = true;
break;
default:
start = false;
}
}
return start;
}
template <class ASTBuilder>
ASTNode parseYieldExpression(ASTBuilder& builder)
{
if (this->context->inParameterParsing) {
this->throwError("Cannot use yield expression within parameters");
}
MetaNode node = this->createNode();
this->expectKeyword(YieldKeyword);
ASTNode exprNode = nullptr;
bool delegate = false;
if (!this->hasLineTerminator) {
const bool previousAllowYield = this->context->allowYield;
this->context->allowYield = false;
delegate = this->match(Multiply);
if (delegate) {
this->nextToken();
exprNode = this->parseAssignmentExpression<ASTBuilder, false>(builder);
} else if (isStartOfExpression()) {
exprNode = this->parseAssignmentExpression<ASTBuilder, false>(builder);
}
this->context->allowYield = previousAllowYield;
}
this->currentScopeContext->m_hasYield = true;
return this->finalize(node, builder.createYieldExpressionNode(exprNode, delegate));
}
// ECMA-262 14.5 Class Definitions
template <class ASTBuilder>
ASTNode parseClassElement(ASTBuilder& builder, ASTNode& constructor, bool hasSuperClass)
{
ALLOC_TOKEN(token);
*token = this->lookahead;
MetaNode node = this->createNode();
ClassElementNode::Kind kind = ClassElementNode::Kind::None;
ASTNode keyNode = nullptr;
ASTNode value = nullptr;
StringView keyString;
bool computed = false;
bool isStatic = false;
bool isGenerator = false;
if (this->match(Multiply)) {
this->nextToken();
} else {
computed = this->match(LeftSquareBracket);
keyNode = this->parseObjectPropertyKey(builder, keyString);
if (token->type == Token::KeywordToken && (this->qualifiedPropertyName(&this->lookahead) || this->match(Multiply)) && token->valueStringLiteral(this->scanner) == "static") {
*token = this->lookahead;
isStatic = true;
computed = this->match(LeftSquareBracket);
if (this->match(Multiply)) {
this->nextToken();
} else {
keyNode = this->parseObjectPropertyKey(builder, keyString);
}
}
}
bool lookaheadPropertyKey = this->qualifiedPropertyName(&this->lookahead);
if (token->type == Token::IdentifierToken) {
if (token->valueStringLiteral(this->scanner) == "get" && lookaheadPropertyKey) {
kind = ClassElementNode::Kind::Get;
computed = this->match(LeftSquareBracket);
keyNode = this->parseObjectPropertyKey(builder, keyString);
this->context->allowYield = false;
value = this->parseGetterMethod(builder);
} else if (token->valueStringLiteral(this->scanner) == "set" && lookaheadPropertyKey) {
kind = ClassElementNode::Kind::Set;
computed = this->match(LeftSquareBracket);
keyNode = this->parseObjectPropertyKey(builder, keyString);
value = this->parseSetterMethod(builder);
}
} else if (lookaheadPropertyKey && token->type == Token::PunctuatorToken && token->valuePunctuatorKind == Multiply) {
kind = ClassElementNode::Kind::Method;
computed = this->match(LeftSquareBracket);
keyNode = this->parseObjectPropertyKey(builder, keyString);
value = this->parseGeneratorMethod(builder);
isGenerator = true;
}
if (kind == ClassElementNode::Kind::None && keyNode && this->match(LeftParenthesis)) {
kind = ClassElementNode::Kind::Method;
bool allowSuperCall = false;
if (hasSuperClass) {
if (keyNode && this->isPropertyKey(keyNode, keyString, "constructor")) {
allowSuperCall = true;
}
}
value = this->parsePropertyMethodFunction(builder, allowSuperCall);
}
if (kind == ClassElementNode::Kind::None) {
this->throwUnexpectedToken(this->lookahead);
}
if (isStatic && !computed && this->isPropertyKey(keyNode, keyString, "prototype")) {
this->throwUnexpectedToken(*token, Messages::StaticPrototype);
}
if (!isStatic && !computed && this->isPropertyKey(keyNode, keyString, "constructor")) {
if (kind != ClassElementNode::Kind::Method) {
this->throwUnexpectedToken(*token, Messages::ConstructorSpecialMethod);
}
if (isGenerator) {
this->throwUnexpectedToken(*token, Messages::ConstructorGenerator);
}
if (constructor) {
this->throwUnexpectedToken(*token, Messages::DuplicateConstructor);
} else {
if (!this->isParsingSingleFunction) {
this->lastPoppedScopeContext->m_functionName = escargotContext->staticStrings().constructor;
this->lastPoppedScopeContext->m_isClassConstructor = true;
this->lastPoppedScopeContext->m_isDerivedClassConstructor = hasSuperClass;
}
constructor = value;
ASTNode empty = nullptr;
return empty;
}
}
if (!this->isParsingSingleFunction) {
if (keyNode->type() == Identifier) {
this->lastPoppedScopeContext->m_functionName = keyNode->asIdentifier()->name();
}
if (isStatic) {
this->lastPoppedScopeContext->m_isClassStaticMethod = true;
} else {
this->lastPoppedScopeContext->m_isClassMethod = true;
}
}
return this->finalize(node, builder.createClassElementNode(keyNode, value, kind, computed, isStatic));
}
template <class ASTBuilder>
ASTNode parseClassBody(ASTBuilder& builder, bool hasSuperClass, MetaNode& endNode)
{
MetaNode node = this->createNode();
ASTNodeVector body;
ASTNode constructor = nullptr;
this->expect(LeftBrace);
while (!this->match(RightBrace)) {
if (this->match(SemiColon)) {
this->nextToken();
} else {
ASTNode classElement = this->parseClassElement(builder, constructor, hasSuperClass);
if (classElement) {
body.push_back(classElement);
}
}
}
endNode = this->createNode();
endNode.index++; // advancing for '{'
this->expect(RightBrace);
return this->finalize(node, builder.createClassBodyNode(std::move(body), constructor));
}
template <class ASTBuilder, typename ClassType>
ASTNode classDeclaration(ASTBuilder& builder, bool identifierIsOptional)
{
bool previousStrict = this->context->strict;
this->context->strict = true;
MetaNode startNode = this->createNode();
this->expectKeyword(ClassKeyword);
ASTNode idNode = nullptr;
AtomicString id;
if (!identifierIsOptional || this->lookahead.type == Token::IdentifierToken) {
idNode = this->parseVariableIdentifier(builder);
id = idNode->asIdentifier()->name();
}
if (!identifierIsOptional && id.string()->length()) {
addDeclaredNameIntoContext(id, this->lexicalBlockIndex, KeywordKind::LetKeyword);
}
bool hasSuperClass = false;
ASTNode superClass = nullptr;
if (this->matchKeyword(ExtendsKeyword)) {
hasSuperClass = true;
this->nextToken();
superClass = this->isolateCoverGrammar(builder, &Parser::parseLeftHandSideExpressionAllowCall<ASTBuilder>);
}
ParserBlockContext classBlockContext = openBlock();
if (id.string()->length()) {
addDeclaredNameIntoContext(id, this->lexicalBlockIndex, KeywordKind::ConstKeyword);
}
MetaNode endNode;
ASTNode classBody = this->parseClassBody(builder, hasSuperClass, endNode);
this->context->strict = previousStrict;
closeBlock(classBlockContext);
return this->finalize(startNode, builder.template createClass<ClassType>(idNode, superClass, classBody, classBlockContext.childLexicalBlockIndex, StringView(this->scanner->source, startNode.index, endNode.index)));
}
template <class ASTBuilder>
ASTNode parseClassDeclaration(ASTBuilder& builder)
{
return classDeclaration<ASTBuilder, ClassDeclarationNode>(builder, false);
}
template <class ASTBuilder>
ASTNode parseClassExpression(ASTBuilder& builder)
{
return classDeclaration<ASTBuilder, ClassExpressionNode>(builder, true);
}
// ECMA-262 15.2 Modules
// ECMA-262 15.2.2 Imports
template <class ASTBuilder>
ASTNode parseModuleSpecifier(ASTBuilder& builder)
{
MetaNode node = this->createNode();
if (this->lookahead.type != Token::StringLiteralToken) {
this->throwError(Messages::InvalidModuleSpecifier);
}
ALLOC_TOKEN(token);
this->nextToken(token);
// const raw = this->getTokenRaw(token);
ASSERT(token->type == Token::StringLiteralToken);
return this->finalize(node, builder.createLiteralNode(token->valueStringLiteralForAST(this->scanner)));
}
// import {<foo as bar>} ...;
template <class ASTBuilder>
ASTNode parseImportSpecifier(ASTBuilder& builder)
{
MetaNode node = this->createNode();
ASTNode local = nullptr;
ASTNode imported = this->parseIdentifierName(builder);
if (this->matchContextualKeyword("as")) {
this->nextToken();
local = this->parseVariableIdentifier(builder);
} else {
local = imported;
}
return this->finalize(node, builder.createImportSpecifierNode(local, imported));
}
// {foo, bar as bas}
template <class ASTBuilder>
ASTNodeVector parseNamedImports(ASTBuilder& builder)
{
this->expect(PunctuatorKind::LeftBrace);
ASTNodeVector specifiers;
while (!this->match(PunctuatorKind::RightBrace)) {
specifiers.push_back(this->parseImportSpecifier(builder));
if (!this->match(PunctuatorKind::RightBrace)) {
this->expect(PunctuatorKind::Comma);
}
}
this->expect(PunctuatorKind::RightBrace);
return specifiers;
}
// import <foo> ...;
template <class ASTBuilder>
ASTNode parseImportDefaultSpecifier(ASTBuilder& builder)
{
MetaNode node = this->createNode();
ASTNode local = this->parseIdentifierName(builder);
return this->finalize(node, builder.createImportDefaultSpecifierNode(local));
}
// import <* as foo> ...;
template <class ASTBuilder>
ASTNode parseImportNamespaceSpecifier(ASTBuilder& builder)
{
MetaNode node = this->createNode();
this->expect(PunctuatorKind::Multiply);
if (!this->matchContextualKeyword("as")) {
this->throwError(Messages::NoAsAfterImportNamespace);
}
this->nextToken();
ASTNode local = this->parseIdentifierName(builder);
return this->finalize(node, builder.createImportNamespaceSpecifierNode(local));
}
template <class ASTBuilder>
ASTNode parseImportDeclaration(ASTBuilder& builder)
{
if (this->context->inFunctionBody) {
this->throwError(Messages::IllegalImportDeclaration);
}
if (this->lexicalBlockIndex != 0) {
this->throwError(Messages::IllegalImportDeclaration);
}
MetaNode node = this->createNode();
this->expectKeyword(KeywordKind::ImportKeyword);
ASTNode src = nullptr;
Script::ImportEntryVector importEntrys;
ASTNodeVector specifiers;
if (this->lookahead.type == Token::StringLiteralToken) {
// import 'foo';
src = this->parseModuleSpecifier(builder);
} else {
if (this->match(PunctuatorKind::LeftBrace)) {
// import {bar}
specifiers = this->parseNamedImports(builder);
if (builder.isNodeGenerator()) {
for (size_t i = 0; i < specifiers.size(); i++) {
Script::ImportEntry entry;
entry.m_importName = specifiers[i]->asImportSpecifier()->imported()->name();
entry.m_localName = specifiers[i]->asImportSpecifier()->local()->name();
importEntrys.push_back(entry);
}
}
} else if (this->match(PunctuatorKind::Multiply)) {
// import * as foo
specifiers.push_back(this->parseImportNamespaceSpecifier(builder));
if (builder.isNodeGenerator()) {
Script::ImportEntry entry;
entry.m_importName = this->escargotContext->staticStrings().asciiTable[(unsigned char)'*'];
entry.m_localName = specifiers.back()->asImportNamespaceSpecifier()->local()->name();
importEntrys.push_back(entry);
}
} else if (this->isIdentifierName(&this->lookahead) && !this->matchKeyword(KeywordKind::DefaultKeyword)) {
// import foo
specifiers.push_back(this->parseImportDefaultSpecifier(builder));
if (builder.isNodeGenerator()) {
Script::ImportEntry entry;
entry.m_importName = this->escargotContext->staticStrings().stringDefault;
entry.m_localName = specifiers.back()->asImportDefaultSpecifier()->local()->name();
importEntrys.push_back(entry);
}
if (this->match(PunctuatorKind::Comma)) {
this->nextToken();
if (this->match(PunctuatorKind::Multiply)) {
// import foo, * as foo
specifiers.push_back(this->parseImportNamespaceSpecifier(builder));
if (builder.isNodeGenerator()) {
Script::ImportEntry entry;
entry.m_importName = this->escargotContext->staticStrings().asciiTable[(unsigned char)'*'];
entry.m_localName = specifiers.back()->asImportNamespaceSpecifier()->local()->name();
importEntrys.push_back(entry);
}
} else if (this->match(PunctuatorKind::LeftBrace)) {
// import foo, {bar}
auto v = this->parseNamedImports(builder);
if (builder.isNodeGenerator()) {
for (size_t i = 0; i < v.size(); i++) {
Script::ImportEntry entry;
entry.m_importName = v[i]->asImportSpecifier()->imported()->name();
entry.m_localName = v[i]->asImportSpecifier()->local()->name();
importEntrys.push_back(entry);
}
specifiers.insert(specifiers.end(), v.begin(), v.end());
}
} else {
this->throwUnexpectedToken(this->lookahead);
}
}
} else {
ALLOC_TOKEN(token);
this->nextToken(token);
this->throwUnexpectedToken(*token);
}
if (!this->matchContextualKeyword("from")) {
this->throwUnexpectedToken(this->lookahead);
}
this->nextToken();
src = this->parseModuleSpecifier(builder);
}
this->consumeSemicolon();
if (builder.isNodeGenerator()) {
for (size_t i = 0; i < importEntrys.size(); i++) {
importEntrys[i].m_moduleRequest = src->asLiteral()->value().asString();
addDeclaredNameIntoContext(importEntrys[i].m_localName, this->lexicalBlockIndex, KeywordKind::ConstKeyword);
this->moduleData->m_importEntries.insert(this->moduleData->m_importEntries.size(), importEntrys[i]);
}
}
return this->finalize(node, builder.createImportDeclarationNode(std::move(specifiers), src));
}
// ECMA-262 15.2.3 Exports
template <class ASTBuilder>
ASTNode parseExportSpecifier(ASTBuilder& builder)
{
MetaNode node = this->createNode();
ASTNode local = this->parseIdentifierName(builder);
ASTNode exported = local;
if (this->matchContextualKeyword("as")) {
this->nextToken();
exported = this->parseIdentifierName(builder);
}
return this->finalize(node, builder.createExportSpecifierNode(local, exported));
}
void addExportDeclarationEntry(Script::ExportEntry ee)
{
// http://www.ecma-international.org/ecma-262/6.0/#sec-parsemodule
// If ee.[[ModuleRequest]] is null, then
if (!ee.m_moduleRequest.hasValue()) {
// If ee.[[LocalName]] is not an element of importedBoundNames, then
size_t localNameExistInImportedBoundNamesIndex = SIZE_MAX;
for (size_t i = 0; i < this->moduleData->m_importEntries.size(); i++) {
if (ee.m_localName == this->moduleData->m_importEntries[i].m_localName) {
localNameExistInImportedBoundNamesIndex = i;
break;
}
}
if (localNameExistInImportedBoundNamesIndex == SIZE_MAX) {
// Append ee to localExportEntries.
this->moduleData->m_localExportEntries.push_back(ee);
} else {
// Let ie be the element of importEntries whose [[LocalName]] is the same as ee.[[LocalName]].
Script::ImportEntry ie = this->moduleData->m_importEntries[localNameExistInImportedBoundNamesIndex];
// If ie.[[ImportName]] is "*", then
if (ie.m_importName == this->escargotContext->staticStrings().asciiTable[(unsigned char)'*']) {
// Assert: this is a re-export of an imported module namespace object.
// Append ee to localExportEntries.
this->moduleData->m_localExportEntries.push_back(ee);
} else {
// Else, this is a re-export of a single name
// Append to indirectExportEntries the Record {[[ModuleRequest]]: ie.[[ModuleRequest]], [[ImportName]]: ie.[[ImportName]], [[LocalName]]: null, [[ExportName]]: ee.[[ExportName]] }.
Script::ExportEntry newEntry;
newEntry.m_moduleRequest = ie.m_moduleRequest;
newEntry.m_importName = ie.m_importName;
newEntry.m_exportName = ee.m_exportName;
this->moduleData->m_indirectExportEntries.push_back(newEntry);
}
}
} else if (ee.m_importName == this->escargotContext->staticStrings().asciiTable[(unsigned char)'*']) {
// Else, if ee.[[ImportName]] is "*", then
this->moduleData->m_starExportEntries.push_back(ee);
} else {
// Append ee to indirectExportEntries.
this->moduleData->m_indirectExportEntries.push_back(ee);
}
}
template <class ASTBuilder>
ASTNode parseExportDeclaration(ASTBuilder& builder)
{
if (this->context->inFunctionBody) {
this->throwError(Messages::IllegalExportDeclaration);
}
if (this->lexicalBlockIndex != 0) {
this->throwError(Messages::IllegalExportDeclaration);
}
MetaNode node = this->createNode();
this->expectKeyword(KeywordKind::ExportKeyword);
ASTNode exportDeclaration = nullptr;
if (this->matchKeyword(KeywordKind::DefaultKeyword)) {
// export default ...
this->nextToken();
if (this->matchKeyword(KeywordKind::FunctionKeyword)) {
// export default function foo () {}
// export default function () {}
ASTNode declaration = this->parseFunctionExpression(builder);
if (builder.isNodeGenerator()) {
Script::ExportEntry entry;
entry.m_exportName = this->escargotContext->staticStrings().stringDefault;
entry.m_localName = declaration->asFunctionExpression()->functionName().string()->length() ? declaration->asFunctionExpression()->functionName() : this->escargotContext->staticStrings().stringStarDefaultStar;
addDeclaredNameIntoContext(entry.m_localName.value(), this->lexicalBlockIndex, KeywordKind::LetKeyword);
addExportDeclarationEntry(entry);
exportDeclaration = this->finalize(node, builder.createExportDefaultDeclarationNode(declaration, entry.m_exportName.value(), entry.m_localName.value()));
}
} else if (this->matchKeyword(KeywordKind::ClassKeyword)) {
// export default class foo {}
auto classNode = this->parseClassExpression(builder);
if (builder.isNodeGenerator()) {
Script::ExportEntry entry;
entry.m_exportName = this->escargotContext->staticStrings().stringDefault;
entry.m_localName = classNode->asClassExpression()->classNode().id() ? classNode->asClassExpression()->classNode().id()->asIdentifier()->name() : this->escargotContext->staticStrings().stringStarDefaultStar;
addDeclaredNameIntoContext(entry.m_localName.value(), this->lexicalBlockIndex, KeywordKind::LetKeyword);
addExportDeclarationEntry(entry);
exportDeclaration = this->finalize(node, builder.createExportDefaultDeclarationNode(classNode, entry.m_exportName.value(), entry.m_localName.value()));
}
} else {
if (this->matchContextualKeyword("from")) {
this->throwUnexpectedToken(this->lookahead);
}
// export default {};
// export default [];
// export default (1 + 2);
Script::ExportEntry entry;
entry.m_exportName = this->escargotContext->staticStrings().stringDefault;
entry.m_localName = this->escargotContext->staticStrings().stringStarDefaultStar;
ASTNode declaration = this->match(PunctuatorKind::LeftBrace) ? this->parseObjectInitializer(builder) : this->match(PunctuatorKind::LeftSquareBracket) ? this->parseArrayInitializer(builder) : this->parseAssignmentExpression<ASTBuilder, true>(builder);
exportDeclaration = this->finalize(node, builder.createExportDefaultDeclarationNode(declaration, entry.m_exportName.value(), entry.m_localName.value()));
addDeclaredNameIntoContext(entry.m_localName.value(), this->lexicalBlockIndex, KeywordKind::LetKeyword);
addExportDeclarationEntry(entry);
this->consumeSemicolon();
}
} else if (this->match(PunctuatorKind::Multiply)) {
// export * from 'foo';
this->nextToken();
if (!this->matchContextualKeyword("from")) {
this->throwUnexpectedToken(this->lookahead);
}
this->nextToken();
if (this->lookahead.type != Token::StringLiteralToken) {
this->throwUnexpectedToken(this->lookahead);
}
ASTNode src = this->parseModuleSpecifier(builder);
if (builder.isNodeGenerator()) {
Script::ExportEntry entry;
entry.m_exportName = this->escargotContext->staticStrings().stringDefault;
entry.m_moduleRequest = src->asLiteral()->value().asString();
entry.m_importName = this->escargotContext->staticStrings().asciiTable[(unsigned char)'*'];
addExportDeclarationEntry(entry);
}
exportDeclaration = this->finalize(node, builder.createExportAllDeclarationNode(src));
this->consumeSemicolon();
} else if (this->lookahead.type == Token::KeywordToken) {
// export var f = 1;
auto oldNameCallback = this->nameDeclaredCallback;
AtomicStringVector declaredNames;
if (builder.isNodeGenerator()) {
this->nameDeclaredCallback = [&declaredNames](AtomicString name, LexicalBlockIndex, KeywordKind, bool) {
declaredNames.push_back(name);
};
}
AtomicStringVector declaredName;
ASTNode declaration = nullptr;
switch (this->lookahead.valueKeywordKind) {
case KeywordKind::LetKeyword:
case KeywordKind::ConstKeyword:
declaration = this->parseLexicalDeclaration(builder, false);
break;
case KeywordKind::VarKeyword:
case KeywordKind::ClassKeyword:
case KeywordKind::FunctionKeyword:
declaration = this->parseStatementListItem(builder);
break;
default:
this->throwUnexpectedToken(this->lookahead);
break;
}
for (size_t i = 0; i < declaredNames.size(); i++) {
Script::ExportEntry entry;
entry.m_exportName = declaredNames[i];
entry.m_localName = declaredNames[i];
addExportDeclarationEntry(entry);
}
exportDeclaration = this->finalize(node, builder.createExportNamedDeclarationNode(declaration, ASTNodeVector(), nullptr));
this->nameDeclaredCallback = oldNameCallback;
} else {
ASTNodeVector specifiers;
ASTNode source = nullptr;
bool isExportFromIdentifier = false;
this->expect(PunctuatorKind::LeftBrace);
while (!this->match(PunctuatorKind::RightBrace)) {
isExportFromIdentifier = isExportFromIdentifier || this->matchKeyword(KeywordKind::DefaultKeyword);
specifiers.push_back(this->parseExportSpecifier(builder));
if (!this->match(PunctuatorKind::RightBrace)) {
this->expect(PunctuatorKind::Comma);
}
}
this->expect(PunctuatorKind::RightBrace);
bool seenFrom = false;
if (this->matchContextualKeyword("from")) {
seenFrom = true;
// export {default} from 'foo';
// export {foo} from 'foo';
this->nextToken();
source = this->parseModuleSpecifier(builder);
this->consumeSemicolon();
} else if (isExportFromIdentifier) {
// export {default}; // missing fromClause
this->throwUnexpectedToken(this->lookahead);
} else {
// export {foo};
this->consumeSemicolon();
}
if (builder.isNodeGenerator()) {
for (size_t i = 0; i < specifiers.size(); i++) {
Script::ExportEntry entry;
if (seenFrom) {
entry.m_exportName = specifiers[i]->asExportSpecifier()->exported()->name();
entry.m_importName = specifiers[i]->asExportSpecifier()->local()->name();
entry.m_moduleRequest = source->asLiteral()->value().asString();
} else {
entry.m_exportName = specifiers[i]->asExportSpecifier()->exported()->name();
entry.m_localName = specifiers[i]->asExportSpecifier()->local()->name();
}
addExportDeclarationEntry(entry);
}
}
exportDeclaration = this->finalize(node, builder.createExportNamedDeclarationNode(nullptr, std::move(specifiers), source));
}
return exportDeclaration;
}
ProgramNode* parseProgram(NodeGenerator& builder)
{
MetaNode startNode = this->createNode();
pushScopeContext(new (this->allocator) ASTFunctionScopeContext(this->allocator, this->context->strict));
this->context->allowLexicalDeclaration = true;
StatementContainer* container = builder.createStatementContainer();
this->parseDirectivePrologues(builder, container);
StatementNode* referNode = nullptr;
while (this->startMarker.index < this->scanner->length) {
referNode = container->appendChild(this->parseStatementListItem(builder), referNode);
}
MetaNode endNode = this->createNode();
this->currentScopeContext->m_bodyEndLOC.index = endNode.index;
#ifndef NDEBUG
this->currentScopeContext->m_bodyEndLOC.line = endNode.line;
this->currentScopeContext->m_bodyEndLOC.column = endNode.column;
#endif
return this->finalize(startNode, builder.createProgramNode(container, this->currentScopeContext, this->moduleData, std::move(this->numeralLiteralVector)));
}
FunctionNode* parseScriptFunction(NodeGenerator& builder)
{
ASSERT(this->isParsingSingleFunction);
MetaNode node = this->createNode();
StatementContainer* params = this->parseFunctionParameters(builder);
BlockStatementNode* body = this->parseFunctionBody(builder);
return this->finalize(node, builder.createFunctionNode(params, body, std::move(this->numeralLiteralVector)));
}
FunctionNode* parseScriptArrowFunction(NodeGenerator& builder, bool hasArrowParameterPlaceHolder)
{
ASSERT(this->isParsingSingleFunction);
MetaNode node = this->createNode();
StatementContainer* params;
BlockStatementNode* body;
// generate parameter statements
if (hasArrowParameterPlaceHolder) {
params = parseFunctionParameters(builder);
} else {
Node* param = this->parseConditionalExpression(builder);
ASSERT(param->type() == Identifier);
params = builder.createStatementContainer();
InitializeParameterExpressionNode* init = this->finalize(node, builder.createInitializeParameterExpressionNode(param, 0));
Node* statement = this->finalize(node, builder.createExpressionStatementNode(init));
params->appendChild(statement);
}
this->expect(Arrow);
this->context->inArrowFunction = true;
// generate body statements
if (this->match(LeftBrace)) {
body = parseFunctionBody(builder);
} else {
MetaNode nodeStart = this->createNode();
StatementContainer* container = builder.createStatementContainer();
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;
bool previousStrict = this->context->strict;
bool previousAllowYield = this->context->allowYield;
this->context->allowYield = true;
Node* expr = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<NodeGenerator, false>);
container->appendChild(this->finalize(nodeStart, builder.createReturnStatementNode(expr)), nullptr);
/*
if (this->context->strict && list.firstRestricted) {
this->throwUnexpectedToken(list.firstRestricted, list.message);
}
if (this->context->strict && list.stricted) {
this->throwUnexpectedToken(list.stricted, list.message);
}
*/
this->context->strict = previousStrict;
this->context->allowYield = previousAllowYield;
this->context->labelSet = previousLabelSet;
this->context->inIteration = previousInIteration;
this->context->inSwitch = previousInSwitch;
this->context->inFunctionBody = previousInFunctionBody;
body = this->finalize(nodeStart, builder.createBlockStatementNode(container));
}
return this->finalize(node, builder.createFunctionNode(params, body, std::move(this->numeralLiteralVector)));
}
};
ProgramNode* parseProgram(::Escargot::Context* ctx, StringView source, bool isModule, bool strictFromOutside, bool inWith, size_t stackRemain, bool allowSuperCallOutside, bool allowSuperPropertyOutside)
{
// GC should be disabled during the parsing process
ASSERT(GC_is_disabled());
ASSERT(ctx->astAllocator().isInitialized());
Parser parser(ctx, source, isModule, stackRemain);
NodeGenerator builder(ctx->astAllocator());
parser.context->strict = strictFromOutside;
parser.context->inWith = inWith;
parser.context->allowSuperCall = allowSuperCallOutside;
parser.context->allowSuperProperty = allowSuperPropertyOutside;
ProgramNode* nd = parser.parseProgram(builder);
return nd;
}
FunctionNode* parseSingleFunction(::Escargot::Context* ctx, InterpretedCodeBlock* codeBlock, ASTFunctionScopeContext*& scopeContext, size_t stackRemain)
{
// GC should be disabled during the parsing process
ASSERT(GC_is_disabled());
ASSERT(ctx->astAllocator().isInitialized());
Parser parser(ctx, codeBlock->src(), false, stackRemain, codeBlock->sourceElementStart());
NodeGenerator builder(ctx->astAllocator());
parser.trackUsingNames = false;
parser.context->allowLexicalDeclaration = true;
parser.context->allowSuperCall = true;
parser.context->allowSuperProperty = true;
parser.isParsingSingleFunction = true;
parser.codeBlock = codeBlock;
scopeContext = new (ctx->astAllocator()) ASTFunctionScopeContext(ctx->astAllocator(), codeBlock->isStrict());
parser.pushScopeContext(scopeContext);
parser.context->allowYield = !codeBlock->isGenerator();
if (codeBlock->isArrowFunctionExpression()) {
return parser.parseScriptArrowFunction(builder, codeBlock->hasArrowParameterPlaceHolder());
}
return parser.parseScriptFunction(builder);
}
} // namespace esprima
} // namespace Escargot