escargot/src/parser/esprima_cpp/esprima.cpp
Patrick Kim 03ac100d7f Optmize escargot (#454)
* Don't allocate new StringView on parser if possible
* Store esprima::Context::firstCoverInitializedNameError as pointer. it can reduce copy cost on esprima::*CoverGrammar
* Make Script as reexcutable if possible
* Don't save parsed source codes
  - Remove SourceStringView
  - When create AtomicString from StringView, we should make new string data instead of reference StringView
* Reduce size of StringBufferAccessData
* Fix crash in toString of FunctionObject.

Signed-off-by: seonghyun kim <sh8281.kim@samsung.com>
2019-10-11 15:32:04 +09:00

5282 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 ASTPassNode typename ASTBuilder::ASTPassNode
#define ASTNodePtr typename ASTBuilder::ASTNodePtr
#define ASTIdentifierNode typename ASTBuilder::ASTIdentifierNode
#define ASTStatementContainer typename ASTBuilder::ASTStatementContainer
#define ASTStatementNodePtr typename ASTBuilder::ASTStatementNodePtr
#define ASTNodeVector typename ASTBuilder::ASTNodeVector
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;
std::unique_ptr<Scanner::ScannerResult> 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;
ExpressionNodeVector 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;
enum SourceType {
Script,
Module
};
SourceType sourceType;
Scanner::ScannerResult lookahead;
Script::ModuleData* moduleData;
Context contextInstance;
Context* context;
CodeBlock* codeBlock;
Marker baseMarker;
Marker startMarker;
Marker lastMarker;
Vector<ASTFunctionScopeContext*, GCUtil::gc_malloc_allocator<ASTFunctionScopeContext*>> scopeContexts;
ASTFunctionScopeContext* lastPoppedScopeContext;
AtomicString lastUsingName;
std::function<void(AtomicString, LexicalBlockIndex, KeywordKind, bool)> nameDeclaredCallback;
bool trackUsingNames;
bool hasLineTerminator;
bool isParsingSingleFunction;
size_t childFunctionIndex;
size_t stackLimit;
size_t subCodeBlockIndex;
LexicalBlockIndex lexicalBlockIndex;
LexicalBlockIndex lexicalBlockCount;
ASTFunctionScopeContext fakeContext;
struct ParseFormalParametersResult {
PatternNodeVector params;
std::vector<AtomicString> paramSet;
Scanner::ScannerResult stricted;
Scanner::ScannerResult firstRestricted;
const char* message;
bool valid;
ParseFormalParametersResult()
: message(nullptr)
, valid(false)
{
}
};
Parser(::Escargot::Context* escargotContext, StringView code, bool isModule, size_t stackRemain, ExtendedNodeLOC startLoc = ExtendedNodeLOC(0, 0, 0))
: scannerInstance(escargotContext, code, startLoc.line, startLoc.column)
{
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 = nullptr;
this->trackUsingNames = true;
this->isParsingSingleFunction = false;
this->codeBlock = nullptr;
this->childFunctionIndex = 0;
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())) {
next->type = Token::KeywordToken;
}
}
this->lastMarker.index = this->scanner->index;
this->lastMarker.lineNumber = this->scanner->lineNumber;
this->lastMarker.lineStart = this->scanner->lineStart;
}
ASTFunctionScopeContext* popScopeContext(const MetaNode& node)
{
auto ret = scopeContexts.back();
scopeContexts.pop_back();
this->lastUsingName = AtomicString();
this->lastPoppedScopeContext = ret;
return ret;
}
void pushScopeContext(ASTFunctionScopeContext* ctx)
{
scopeContexts.push_back(ctx);
this->lastUsingName = AtomicString();
}
ALWAYS_INLINE void insertUsingName(AtomicString name)
{
if (this->lastUsingName == name) {
return;
}
scopeContexts.back()->insertUsingName(name, this->lexicalBlockIndex);
this->lastUsingName = name;
}
void extractNamesFromFunctionParams(const ParseFormalParametersResult& paramsResult)
{
if (this->isParsingSingleFunction) {
return;
}
const PatternNodeVector& params = paramsResult.params;
const std::vector<AtomicString>& paramNames = paramsResult.paramSet;
bool hasParameterOtherThanIdentifier = false;
#ifndef NDEBUG
bool shouldHaveEqualNumberOfParameterListAndParameterName = true;
#endif
scopeContexts.back()->m_parameters.resizeWithUninitializedValues(params.size());
for (size_t i = 0; i < params.size(); i++) {
switch (params[i]->type()) {
case Identifier: {
scopeContexts.back()->m_parameters[i] = params[i]->asIdentifier()->name();
break;
}
case AssignmentPattern: {
hasParameterOtherThanIdentifier = true;
AtomicString id;
if (params[i]->asAssignmentPattern()->left()->isIdentifier()) {
id = params[i]->asAssignmentPattern()->left()->asIdentifier()->name();
}
#ifndef NDEBUG
else {
shouldHaveEqualNumberOfParameterListAndParameterName = false;
}
#endif
scopeContexts.back()->m_parameters[i] = id;
break;
}
case ArrayPattern:
case ObjectPattern: {
hasParameterOtherThanIdentifier = true;
#ifndef NDEBUG
shouldHaveEqualNumberOfParameterListAndParameterName = false;
#endif
scopeContexts.back()->m_parameters[i] = AtomicString();
break;
}
case RestElement: {
hasParameterOtherThanIdentifier = true;
#ifndef NDEBUG
shouldHaveEqualNumberOfParameterListAndParameterName = false;
#endif
scopeContexts.back()->m_parameters.erase(i);
break;
}
default: {
RELEASE_ASSERT_NOT_REACHED();
}
}
}
#ifndef NDEBUG
if (shouldHaveEqualNumberOfParameterListAndParameterName) {
ASSERT(params.size() == paramNames.size());
}
#endif
scopeContexts.back()->m_hasParameterOtherThanIdentifier = hasParameterOtherThanIdentifier;
for (size_t i = 0; i < paramNames.size(); i++) {
scopeContexts.back()->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");
}
}
}
}
}
void pushScopeContext(AtomicString functionName)
{
this->subCodeBlockIndex++;
if (this->isParsingSingleFunction) {
fakeContext = ASTFunctionScopeContext();
pushScopeContext(&fakeContext);
return;
}
auto parentContext = scopeContexts.back();
pushScopeContext(new ASTFunctionScopeContext(this->context->strict));
scopeContexts.back()->m_functionName = functionName;
scopeContexts.back()->m_inWith = this->context->inWith;
if (parentContext) {
parentContext->m_childScopes.push_back(scopeContexts.back());
}
}
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(Scanner::ScannerResult* token, const char* message = nullptr)
{
ASSERT(token != nullptr);
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())) {
msg = Messages::UnexpectedReserved;
} else if (this->context->strict && this->scanner->isStrictModeReservedWord(token->relatedSource())) {
msg = Messages::StrictReservedWord;
}
}
} else if (token->type == Token::EOFToken) {
msg = Messages::UnexpectedEOS;
}
value = (token->type == Token::TemplateToken) ? (String*)new UTF16String(std::move(token->valueTemplate->valueRaw)) : (String*)new StringView(token->relatedSource());
} 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())) {
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;
}
template <typename T>
ALWAYS_INLINE PassRefPtr<T> finalize(MetaNode meta, T* node)
{
/*
auto type = node->type;
if (type == CallExpression) {
CallExpressionNode* c = (CallExpressionNode*)node;
if (c->callee() && c->callee()->isIdentifier() && ((IdentifierNode*)c->callee())->name() == this->escargotContext->staticStrings().eval) {
scopeContexts.back()->m_hasEval = true;
}
}
*/
node->m_loc = NodeLOC(meta.index);
return adoptRef(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.valueStringLiteralData.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()
{
Scanner::ScannerResult 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 Vector<Scanner::ScannerResult, GCUtil::gc_malloc_allocator<Scanner::ScannerResult>> ScannerResultVector;
struct IsolateCoverGrammarContext {
bool previousIsBindingElement;
bool previousIsAssignmentTarget;
std::unique_ptr<Scanner::ScannerResult> 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 = std::move(this->context->firstCoverInitializedNameError);
this->context->isBindingElement = true;
this->context->isAssignmentTarget = true;
checkRecursiveLimit();
}
void endIsolateCoverGrammar(IsolateCoverGrammarContext* grammarContext)
{
ASSERT(grammarContext != nullptr);
if (UNLIKELY((bool)this->context->firstCoverInitializedNameError)) {
this->throwUnexpectedToken(this->context->firstCoverInitializedNameError.get());
}
this->context->isBindingElement = grammarContext->previousIsBindingElement;
this->context->isAssignmentTarget = grammarContext->previousIsAssignmentTarget;
this->context->firstCoverInitializedNameError = std::move(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((bool)grammarContext->previousFirstCoverInitializedNameError)) {
this->context->firstCoverInitializedNameError = std::move(grammarContext->previousFirstCoverInitializedNameError);
}
}
template <class ASTBuilder, typename T>
ALWAYS_INLINE ASTPassNode isolateCoverGrammar(ASTBuilder& builder, T parseFunction)
{
IsolateCoverGrammarContext grammarContext;
startCoverGrammar(&grammarContext);
ASTPassNode result = (this->*parseFunction)(builder);
endIsolateCoverGrammar(&grammarContext);
return result;
}
template <class ASTBuilder, typename T>
ALWAYS_INLINE ASTPassNode isolateCoverGrammarWithFunctor(ASTBuilder& builder, T parseFunction)
{
IsolateCoverGrammarContext grammarContext;
startCoverGrammar(&grammarContext);
ASTPassNode result = parseFunction();
endIsolateCoverGrammar(&grammarContext);
return result;
}
template <class ASTBuilder, typename T>
ALWAYS_INLINE ASTPassNode inheritCoverGrammar(ASTBuilder& builder, T parseFunction)
{
IsolateCoverGrammarContext grammarContext;
startCoverGrammar(&grammarContext);
ASTPassNode result = (this->*parseFunction)(builder);
endInheritCoverGrammar(&grammarContext);
return result;
}
template <class ASTBuilder>
ASTIdentifierNode finishIdentifier(ASTBuilder& builder, Scanner::ScannerResult* token)
{
ASSERT(token != nullptr);
ASTIdentifierNode ret;
StringView sv = token->valueStringLiteral();
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->name());
}
return ret;
}
// ECMA-262 12.2 Primary Expressions
template <class ASTBuilder>
ASTPassNode 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 (token->type == Token::NumericLiteralToken) {
if (this->context->inLoop || token->valueNumber == 0)
this->scopeContexts.back()->insertNumeralLiteral(Value(token->valueNumber));
return this->finalize(node, builder.createLiteralNode(Value(token->valueNumber)));
} else {
return this->finalize(node, builder.createLiteralNode(token->valueStringLiteralForAST()));
}
}
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() == "true";
this->scopeContexts.back()->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);
this->scopeContexts.back()->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 ASTPassNode();
}
void validateParam(ParseFormalParametersResult& options, const Scanner::ScannerResult* param, AtomicString name)
{
ASSERT(param != nullptr);
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>
ASTPassNode parseRestElement(ASTBuilder& builder, ScannerResultVector& 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.get()));
}
template <class ASTBuilder>
ASTPassNode parseBindingRestElement(ASTBuilder& builder, ScannerResultVector& 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.get()));
}
// ECMA-262 13.3.3 Destructuring Binding Patterns
template <class ASTBuilder>
ASTPassNode parseArrayPattern(ASTBuilder& builder, ScannerResultVector& 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>
ASTPassNode parsePropertyPattern(ASTBuilder& builder, ScannerResultVector& params, KeywordKind kind = KeywordKindEnd, bool isExplicitVariableDeclaration = false)
{
MetaNode node = this->createNode();
bool computed = false;
bool shorthand = false;
bool method = false;
ASTNode keyNode; //'': Node.PropertyKey;
ASTNode valueNode; //: 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.get(), expr.get()));
} 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.get(), valueNode.get(), PropertyNode::Kind::Init, computed, shorthand));
}
template <class ASTBuilder>
ASTPassNode parseObjectPattern(ASTBuilder& builder, ScannerResultVector& 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>
ASTPassNode parsePattern(ASTBuilder& builder, ScannerResultVector& 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>
ASTPassNode parsePatternWithDefault(ASTBuilder& builder, ScannerResultVector& params, KeywordKind kind = KeywordKindEnd, bool isExplicitVariableDeclaration = false)
{
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
ASTPassNode pattern = this->parsePattern(builder, params, kind, isExplicitVariableDeclaration);
if (this->match(PunctuatorKind::Substitution)) {
this->nextToken();
const bool previousAllowYield = this->context->allowYield;
ASTPassNode right = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
return this->finalize(this->startNode(startToken), builder.createAssignmentPatternNode(pattern.get(), right.get()));
}
return pattern;
}
bool parseFormalParameter(ParseFormalParametersResult& options)
{
RefPtr<Node> param;
bool trackUsingNamesBefore = this->trackUsingNames;
this->trackUsingNames = false;
ScannerResultVector params;
ALLOC_TOKEN(token);
*token = this->lookahead;
// FIXME allocate temporal NodeGenerator because current parameter parsing is done by NodeGenerator
NodeGenerator builder;
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->validateParam(options, &params[i], as);
}
options.params.push_back(param);
this->trackUsingNames = trackUsingNamesBefore;
return !this->match(PunctuatorKind::RightParenthesis);
}
ParseFormalParametersResult parseFormalParameters(Scanner::ScannerResult* firstRestricted = nullptr)
{
this->context->inParameterParsing = true;
ParseFormalParametersResult options;
if (firstRestricted) {
options.firstRestricted = *firstRestricted;
}
size_t oldSubCodeBlockIndex = this->subCodeBlockIndex;
if (!this->match(RightParenthesis)) {
options.paramSet.clear();
while (this->startMarker.index < this->scanner->length) {
if (!this->parseFormalParameter(options)) {
break;
}
this->expect(Comma);
}
}
this->expect(RightParenthesis);
this->subCodeBlockIndex = oldSubCodeBlockIndex;
this->context->inParameterParsing = false;
options.valid = true;
return options;
}
// ECMA-262 12.2.5 Array Initializer
template <class ASTBuilder, bool checkLeftHasRestrictedWord>
ASTPassNode parseSpreadElement(ASTBuilder& builder)
{
MetaNode node = this->createNode();
this->expect(PunctuatorKind::PeriodPeriodPeriod);
ASTNode arg;
arg = this->inheritCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, checkLeftHasRestrictedWord>);
if (arg->isAssignmentOperation()) {
this->throwError(Messages::DefaultRestParameter);
}
return this->finalize(node, builder.createSpreadElementNode(arg.get()));
}
template <class ASTBuilder>
ASTPassNode 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>
ASTPassNode parsePropertyMethod(ASTBuilder& builder, ParseFormalParametersResult& params)
{
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
const bool previousStrict = this->context->strict;
ASTPassNode 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>
ASTPassNode parsePropertyMethodFunction(ASTBuilder& builder, bool allowSuperCall)
{
const bool isGenerator = false;
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;
}
MetaNode node = this->createNode();
this->expect(LeftParenthesis);
pushScopeContext(AtomicString());
ParseFormalParametersResult params = this->parseFormalParameters();
extractNamesFromFunctionParams(params);
ASTNode method = this->parsePropertyMethod(builder, params);
this->context->allowYield = previousAllowYield;
this->context->inArrowFunction = previousInArrowFunction;
this->context->allowSuperProperty = previousAllowSuperProperty;
this->context->allowSuperCall = previousAllowSuperCall;
scopeContexts.back()->m_paramsStartLOC.index = node.index;
scopeContexts.back()->m_paramsStartLOC.column = node.column;
scopeContexts.back()->m_paramsStartLOC.line = node.line;
scopeContexts.back()->m_allowSuperProperty = true;
if (allowSuperCall) {
scopeContexts.back()->m_allowSuperCall = true;
}
scopeContexts.back()->m_nodeType = ASTNodeType::FunctionExpression;
scopeContexts.back()->m_isGenerator = isGenerator;
return this->finalize(node, builder.createFunctionExpressionNode(popScopeContext(node), isGenerator, this->subCodeBlockIndex));
}
template <class ASTBuilder>
ASTPassNode parseObjectPropertyKey(ASTBuilder& builder, StringView& keyString)
{
MetaNode node = this->createNode();
ALLOC_TOKEN(token);
this->nextToken(token);
ASTNode key;
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 (token->type == Token::NumericLiteralToken) {
if (this->context->inLoop || token->valueNumber == 0)
this->scopeContexts.back()->insertNumeralLiteral(Value(token->valueNumber));
v = Value(token->valueNumber);
} else {
keyString = token->valueStringLiteral();
v = Value(token->valueStringLiteralForAST());
}
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>
ASTPassNode parseObjectProperty(ASTBuilder& builder, bool& hasProto) //: Node.Property
{
ALLOC_TOKEN(token);
*token = this->lookahead;
MetaNode node = this->createNode();
PropertyNode::Kind kind;
ASTNode keyNode; //'': Node.PropertyKey;
ASTNode valueNode; //: 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();
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.get(), 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.reset(new Scanner::ScannerResult(this->lookahead));
this->nextToken();
shorthand = true;
ASTNode init = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
valueNode = this->finalize(node, builder.createAssignmentPatternNode(keyNode.get(), init.get()));
} 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.get(), valueNode.get(), kind, computed, shorthand));
}
template <class ASTBuilder>
ASTPassNode 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>
ASTPassNode 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>
ASTPassNode parseGroupExpression(ASTBuilder& builder)
{
ASTNode exprNode;
this->expect(LeftParenthesis);
if (this->match(RightParenthesis)) {
this->nextToken();
if (!this->match(Arrow)) {
this->expect(Arrow);
}
// FIXME finalize really necessary for ArrowParameterHolderNode?
exprNode = this->finalize(this->createNode(), builder.createArrowParameterPlaceHolderNode());
} else {
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
ScannerResultVector 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.get()));
} 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].get());
}
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].get());
}
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.get()));
}
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].get());
}
} else {
exprNode = builder.reinterpretExpressionAsPattern(exprNode.get());
}
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.release();
}
// 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;
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>
ASTPassNode 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>
ASTPassNode parseNewExpression(ASTBuilder& builder)
{
this->nextToken();
if (this->match(Period)) {
this->nextToken();
if (this->lookahead.type == Token::IdentifierToken && this->context->inFunctionBody && this->lookahead.relatedSource() == "target") {
this->nextToken();
scopeContexts.back()->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.get(), std::move(args)));
}
template <class ASTBuilder>
ASTPassNode parseLeftHandSideExpressionAllowCall(ASTBuilder& builder)
{
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
bool previousAllowIn = this->context->allowIn;
this->context->allowIn = true;
ASTNode exprNode;
if (this->context->inFunctionBody && this->matchKeyword(SuperKeyword)) {
throwIfSuperOperationIsNotAllowed();
scopeContexts.back()->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.get(), property.get(), 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) {
scopeContexts.back()->m_hasEval = true;
}
exprNode = this->finalize(this->startNode(startToken), builder.createCallExpressionNode(exprNode.get(), 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.get(), property.get(), 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.get(), quasi.get())), scopeContexts.back(), escargotContext->staticStrings().raw);
} else {
break;
}
}
this->context->allowIn = previousAllowIn;
return exprNode.release();
}
template <class ASTBuilder>
ASTPassNode parseSuper(ASTBuilder& builder)
{
MetaNode node = this->createNode();
this->expectKeyword(SuperKeyword);
if (!this->match(LeftSquareBracket) && !this->match(Period)) {
this->throwUnexpectedToken(&this->lookahead);
}
scopeContexts.back()->m_hasSuperOrNewTarget = true;
return this->finalize(node, builder.createSuperExpressionNode(false));
}
template <class ASTBuilder>
ASTPassNode parseLeftHandSideExpression(ASTBuilder& builder)
{
// assert(this->context->allowIn, 'callee of new expression always allow in keyword.');
ASSERT(this->context->allowIn);
ASTNode exprNode;
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.get(), property.get(), 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.get(), property.get(), 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.get(), quasi.get())), scopeContexts.back(), escargotContext->staticStrings().raw);
} else {
break;
}
}
return exprNode.release();
}
// ECMA-262 12.4 Update Expressions
template <class ASTBuilder>
ASTPassNode parseUpdateExpression(ASTBuilder& builder)
{
ASTNode exprNode;
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.get()));
} else {
exprNode = this->finalize(node, builder.createUpdateExpressionDecrementPrefixNode(exprNode.get()));
}
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.get()));
} else {
exprNode = this->finalize(this->startNode(startToken), builder.createUpdateExpressionDecrementPostfixNode(exprNode.get()));
}
}
}
return exprNode.release();
}
// ECMA-262 12.5 Unary Operators
template <class ASTBuilder>
ASTPassNode parseUnaryExpression(ASTBuilder& builder)
{
if (this->lookahead.type == Token::PunctuatorToken) {
auto punctuatorsKind = this->lookahead.valuePunctuatorKind;
ASTNode exprNode;
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.get()));
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
return exprNode.release();
} 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.get()));
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
return exprNode.release();
} 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.get()));
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
return exprNode.release();
} 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.get()));
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
return exprNode.release();
}
}
bool isKeyword = this->lookahead.type == Token::KeywordToken;
if (isKeyword) {
ASTNode exprNode;
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.get()));
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
return exprNode.release();
} 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.get()));
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
return exprNode.release();
} 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.get()));
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
return exprNode.release();
}
}
return this->parseUpdateExpression(builder);
}
template <class ASTBuilder>
ASTPassNode 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>
ASTPassNode 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;
// FIXME optimize ScannerResult vector and its related operation
ScannerResultVector markers;
markers.reserve(120);
markers.push_back(*startToken);
markers.push_back(this->lookahead);
ASTNode left = expr;
ASTNode right = this->isolateCoverGrammar(builder, &Parser::parseExponentiationExpression<ASTBuilder>);
ASTNodeVector stack;
ScannerResultVector tokenStack;
tokenStack.reserve(120);
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::ScannerResult 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.get(), right.get(), &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].get(), expr.get(), &tokenStack.back()));
markers.pop_back();
tokenStack.pop_back();
i--;
}
RELEASE_ASSERT(i == 0);
}
return expr.release();
}
template <class ASTBuilder>
ASTNodePtr finishBinaryExpression(ASTBuilder& builder, ASTNodePtr left, ASTNodePtr right, Scanner::ScannerResult* 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>
ASTPassNode parseConditionalExpression(ASTBuilder& builder)
{
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
ASTNode exprNode;
exprNode = this->inheritCoverGrammar(builder, &Parser::parseBinaryExpression<ASTBuilder>);
if (this->match(GuessMark)) {
ASTNode consequent;
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.get(), consequent.get(), alternate.get()));
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
}
return exprNode.release();
}
// ECMA-262 12.15 Assignment Operators
// FIXME remove these reparseConditionalExpression function
RefPtr<Node> reparseConditionalExpression(Marker startMarker, RefPtr<Node> expr)
{
return expr;
}
RefPtr<Node> reparseConditionalExpression(Marker startMarker, SyntaxNode expr)
{
this->scanner->index = startMarker.index;
this->scanner->lineNumber = startMarker.lineNumber;
this->scanner->lineStart = startMarker.lineStart;
this->nextToken();
NodeGenerator tempBuilder;
return this->parseConditionalExpression(tempBuilder);
}
template <class ASTBuilder, bool checkLeftHasRestrictedWord>
ASTPassNode parseAssignmentExpression(ASTBuilder& builder)
{
ASTNode exprNode;
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;
// FIXME remove repeated parseConditionalExpression call
RefPtr<Node> exprParamNode = reparseConditionalExpression(startMarker, exprNode);
pushScopeContext(AtomicString());
ParseFormalParametersResult list;
// FIXME reinterpretAsCoverFormalsList
if (type == Identifier) {
ASSERT(exprParamNode->isIdentifier());
this->validateParam(list, &this->lookahead, exprParamNode->asIdentifier()->name());
list.params.push_back(exprParamNode);
list.valid = true;
} else {
this->scanner->index = startMarker.index;
this->scanner->lineNumber = startMarker.lineNumber;
this->scanner->lineStart = startMarker.lineStart;
this->nextToken();
this->expect(LeftParenthesis);
scopeContexts.back()->m_hasArrowParameterPlaceHolder = true;
list = this->parseFormalParameters();
}
exprNode.release();
// FIXME remove validity check?
if (list.valid) {
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;
scopeContexts.back()->m_allowSuperCall = this->context->allowSuperCall;
scopeContexts.back()->m_allowSuperProperty = this->context->allowSuperProperty;
scopeContexts.back()->m_paramsStartLOC.index = startNode.index;
scopeContexts.back()->m_paramsStartLOC.column = startNode.column;
scopeContexts.back()->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(builder);
} else {
if (this->isParsingSingleFunction) {
// when parsing for function call,
// assignmentExpression should parse(scan) only child arrow functions
ASSERT(this->childFunctionIndex > 0);
size_t realIndex = this->childFunctionIndex - 1;
this->childFunctionIndex++;
InterpretedCodeBlock* currentTarget = this->codeBlock->asInterpretedCodeBlock();
size_t orgIndex = this->lookahead.start;
StringView src = currentTarget->childBlocks()[realIndex]->bodySrc();
this->scanner->index = src.length() + orgIndex;
this->scanner->lineNumber = currentTarget->childBlocks()[realIndex]->sourceElementStart().line;
this->scanner->lineStart = currentTarget->childBlocks()[realIndex]->sourceElementStart().index - currentTarget->childBlocks()[realIndex]->sourceElementStart().column;
this->lookahead.lineNumber = this->scanner->lineNumber;
this->lookahead.lineStart = this->scanner->lineStart;
this->nextToken();
} else {
ASSERT(!this->isParsingSingleFunction);
LexicalBlockIndex lexicalBlockIndexBefore = this->lexicalBlockIndex;
LexicalBlockIndex lexicalBlockCountBefore = this->lexicalBlockCount;
this->lexicalBlockIndex = 0;
this->lexicalBlockCount = 0;
size_t oldSubCodeBlockIndex = this->subCodeBlockIndex;
this->subCodeBlockIndex = 0;
auto oldNameCallback = this->nameDeclaredCallback;
if (this->isParsingSingleFunction) {
ASSERT(this->childFunctionIndex > 0);
this->childFunctionIndex++;
}
// parsing arrow function body by SyntaxChecker only at this point
SyntaxChecker newBuilder;
this->isolateCoverGrammar(newBuilder, &Parser::parseAssignmentExpression<SyntaxChecker, false>);
this->subCodeBlockIndex = oldSubCodeBlockIndex;
this->lexicalBlockIndex = lexicalBlockIndexBefore;
this->lexicalBlockCount = lexicalBlockCountBefore;
this->nameDeclaredCallback = oldNameCallback;
scopeContexts.back()->m_bodyStartLOC.line = bodyStart.line;
scopeContexts.back()->m_bodyStartLOC.column = bodyStart.column;
scopeContexts.back()->m_bodyStartLOC.index = bodyStart.index;
scopeContexts.back()->m_bodyEndLOC.index = this->lastMarker.index;
#ifndef NDEBUG
scopeContexts.back()->m_bodyEndLOC.line = this->lastMarker.lineNumber;
scopeContexts.back()->m_bodyEndLOC.column = this->lastMarker.index - this->lastMarker.lineStart;
#endif
}
}
this->scopeContexts.back()->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->scopeContexts.back()->m_isArrowFunctionExpression = true;
this->scopeContexts.back()->m_nodeType = ASTNodeType::ArrowFunctionExpression;
this->scopeContexts.back()->m_isGenerator = false;
exprNode = this->finalize(node, builder.createArrowFunctionExpressionNode(popScopeContext(node), subCodeBlockIndex)); //TODO
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.get());
if (exprNode->isLiteral() || exprNode->type() == ASTNodeType::ThisExpression) {
this->throwError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
}
}
this->nextToken(token);
ASTNode rightNode;
ASTNodePtr exprResult;
rightNode = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
switch (token->valuePunctuatorKind) {
case Substitution:
exprResult = builder.createAssignmentExpressionSimpleNode(exprNode.get(), rightNode.get());
break;
case PlusEqual:
exprResult = builder.createAssignmentExpressionPlusNode(exprNode.get(), rightNode.get());
break;
case MinusEqual:
exprResult = builder.createAssignmentExpressionMinusNode(exprNode.get(), rightNode.get());
break;
case MultiplyEqual:
exprResult = builder.createAssignmentExpressionMultiplyNode(exprNode.get(), rightNode.get());
break;
case DivideEqual:
exprResult = builder.createAssignmentExpressionDivisionNode(exprNode.get(), rightNode.get());
break;
case ModEqual:
exprResult = builder.createAssignmentExpressionModNode(exprNode.get(), rightNode.get());
break;
case LeftShiftEqual:
exprResult = builder.createAssignmentExpressionLeftShiftNode(exprNode.get(), rightNode.get());
break;
case RightShiftEqual:
exprResult = builder.createAssignmentExpressionSignedRightShiftNode(exprNode.get(), rightNode.get());
break;
case UnsignedRightShiftEqual:
exprResult = builder.createAssignmentExpressionUnsignedRightShiftNode(exprNode.get(), rightNode.get());
break;
case BitwiseXorEqual:
exprResult = builder.createAssignmentExpressionBitwiseXorNode(exprNode.get(), rightNode.get());
break;
case BitwiseAndEqual:
exprResult = builder.createAssignmentExpressionBitwiseAndNode(exprNode.get(), rightNode.get());
break;
case BitwiseOrEqual:
exprResult = builder.createAssignmentExpressionBitwiseOrNode(exprNode.get(), rightNode.get());
break;
default:
RELEASE_ASSERT_NOT_REACHED();
}
exprNode = this->finalize(this->startNode(startToken), exprResult);
this->context->firstCoverInitializedNameError.reset();
}
}
}
return exprNode.release();
}
// ECMA-262 12.16 Comma Operator
template <class ASTBuilder>
ASTPassNode parseExpression(ASTBuilder& builder)
{
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
ASTNode exprNode;
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.release();
}
// ECMA-262 13.2 Block
template <class ASTBuilder>
ASTPassNode parseStatementListItem(ASTBuilder& builder)
{
ASTNode statement;
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.release();
}
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);
}
ParserBlockContext ctx;
this->lexicalBlockCount++;
ctx.lexicalBlockCountBefore = this->lexicalBlockCount;
ctx.lexicalBlockIndexBefore = this->lexicalBlockIndex;
ctx.childLexicalBlockIndex = this->lexicalBlockCount;
this->scopeContexts.back()->insertBlockScope(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->scopeContexts.back();
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;
for (size_t i = 0; i < currentFunctionScope->m_childScopes.size(); i++) {
if (currentFunctionScope->m_childScopes[i]->m_nodeType == ASTNodeType::FunctionDeclaration
&& currentFunctionScope->m_childScopes[i]->m_lexicalBlockIndexFunctionLocatedIn == currentBlockIndex) {
isThereFunctionExists = true;
}
}
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;
}
}
for (size_t i = 0; i < currentFunctionScope->m_childScopes.size(); i++) {
if (currentFunctionScope->m_childScopes[i]->m_lexicalBlockIndexFunctionLocatedIn == currentBlockIndex) {
currentFunctionScope->m_childScopes[i]->m_lexicalBlockIndexFunctionLocatedIn = parentBlockIndex;
}
}
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;
}
template <class ASTBuilder>
ASTPassNode parseBlock(ASTBuilder& builder)
{
this->expect(LeftBrace);
ASTStatementContainer block;
ASTStatementNodePtr referNode = nullptr;
ParserBlockContext blockContext = openBlock();
bool allowLexicalDeclarationBefore = this->context->allowLexicalDeclaration;
this->context->allowLexicalDeclaration = true;
block = builder.createStatementContainer();
while (true) {
if (this->match(RightBrace)) {
break;
}
referNode = block->appendChild(this->parseStatementListItem(builder).get(), referNode);
}
this->expect(RightBrace);
this->context->allowLexicalDeclaration = allowLexicalDeclarationBefore;
closeBlock(blockContext);
MetaNode node = this->createNode();
return this->finalize(node, builder.createBlockStatementNode(block.get(), blockContext.childLexicalBlockIndex));
}
// ECMA-262 13.3.1 Let and Const Declarations
template <class ASTBuilder>
ASTPassNode parseLexicalBinding(ASTBuilder& builder, KeywordKind kind, bool inFor)
{
auto node = this->createNode();
ScannerResultVector params;
ASTNode idNode;
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;
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.get(), init.get()));
}
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>
ASTPassNode 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>
ASTPassNode 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->throwUnexpectedToken(token, Messages::StrictReservedWord);
} else {
if (this->context->strict || token->relatedSource() != "let" || (kind != VarKeyword)) {
this->throwUnexpectedToken(token);
}
}
} else if (this->sourceType == Module && token->type == Token::IdentifierToken && token->relatedSource() == "await") {
this->throwUnexpectedToken(token);
}
ASTIdentifierNode id = finishIdentifier(builder, token);
if (kind == KeywordKind::VarKeyword || kind == KeywordKind::LetKeyword || kind == KeywordKind::ConstKeyword) {
addDeclaredNameIntoContext(id->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->scopeContexts.back()->insertVarName(name, blockIndex, true, kind == VarKeyword);
return;
}
}
}
/* code end */
if (!this->scopeContexts.back()->canDeclareName(name, blockIndex, kind == VarKeyword)) {
this->throwError(Messages::Redeclaration, new ASCIIString("Identifier"), name.string());
}
if (kind == VarKeyword) {
this->scopeContexts.back()->insertVarName(name, blockIndex, true, true);
} else {
this->scopeContexts.back()->insertNameAtBlock(name, blockIndex, kind == ConstKeyword);
this->scopeContexts.back()->m_needsToComputeLexicalBlockStuffs = true;
}
}
if (nameDeclaredCallback) {
nameDeclaredCallback(name, blockIndex, kind, isExplicitVariableDeclaration);
}
}
template <class ASTBuilder>
ASTPassNode parseVariableDeclaration(ASTBuilder& builder, DeclarationOptions& options, bool& hasInit, ASTNodeType& leftSideType)
{
ScannerResultVector params;
ASTNode idNode;
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;
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.get(), initNode.get()));
}
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>
ASTPassNode 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>
ASTPassNode 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>
ASTPassNode parseExpressionStatement(ASTBuilder& builder)
{
MetaNode node = this->createNode();
ASTNode expr = this->parseExpression(builder);
this->consumeSemicolon();
return this->finalize(node, builder.createExpressionStatementNode(expr.get()));
}
// ECMA-262 13.6 If statement
template <class ASTBuilder>
ASTPassNode parseIfStatement(ASTBuilder& builder)
{
ASTNode test;
ASTNode consequent;
ASTNode alternate;
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.get(), consequent.get(), alternate.get()));
}
// ECMA-262 13.7.2 The do-while Statement
template <class ASTBuilder>
ASTPassNode 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;
body = this->parseStatement(builder, false);
this->context->inIteration = previousInIteration;
this->context->allowLexicalDeclaration = allowLexicalDeclarationBefore;
this->expectKeyword(WhileKeyword);
this->expect(LeftParenthesis);
ASTNode test;
test = this->parseExpression(builder);
this->expect(RightParenthesis);
if (this->match(SemiColon)) {
this->nextToken();
}
return this->finalize(this->createNode(), builder.createDoWhileStatementNode(test.get(), body.get()));
}
// ECMA-262 13.7.3 The while Statement
template <class ASTBuilder>
ASTPassNode 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;
test = this->parseExpression(builder);
this->expect(RightParenthesis);
bool previousInIteration = this->context->inIteration;
this->context->inIteration = true;
ASTNode body;
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.get(), body.get()));
}
// 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>
ASTPassNode parseForStatement(ASTBuilder& builder)
{
ASTNode init;
ASTNode test;
ASTNode update;
ASTNode left;
ASTNode right;
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() == "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())));
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() == "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.get());
left = init;
init = nullptr;
type = statementTypeForIn;
} else if (this->lookahead.type == Token::IdentifierToken && this->lookahead.relatedSource() == "of") {
if (!this->context->isAssignmentTarget || (initNodeType >= ASTNodeType::AssignmentExpression && initNodeType <= ASTNodeType::AssignmentExpressionSimple)) {
this->throwError(Messages::InvalidLHSInForLoop);
}
this->nextToken();
init = builder.reinterpretExpressionAsPattern(init.get());
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->scopeContexts.back()->findBlockFromBackward(headBlockContext.childLexicalBlockIndex);
if (headBlockContextInstance->m_names.size()) {
// if there are names on headContext (let, const)
// we should copy names into to iterationBlock
this->scopeContexts.back()->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;
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) {
ASTNodePtr forNode = builder.createForStatementNode(init.get(), test.get(), update.get(), body.get(), isLexicalDeclaration, headBlockContext.childLexicalBlockIndex, iterationBlockContext.childLexicalBlockIndex);
return this->finalize(node, forNode);
}
if (type == statementTypeForIn) {
ASTNodePtr forInNode = builder.createForInOfStatementNode(left.get(), right.get(), body.get(), true, isLexicalDeclaration, headBlockContext.childLexicalBlockIndex, iterationBlockContext.childLexicalBlockIndex);
return this->finalize(node, forInNode);
}
ASSERT(type == statementTypeForOf);
ASTNodePtr forOfNode = builder.createForInOfStatementNode(left.get(), right.get(), body.get(), 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>
ASTPassNode 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>
ASTPassNode 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>
ASTPassNode 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;
if (hasArgument) {
argument = this->parseExpression(builder);
}
this->consumeSemicolon();
return this->finalize(this->createNode(), builder.createReturnStatementNode(argument.get()));
}
// ECMA-262 13.11 The with statement
template <class ASTBuilder>
ASTPassNode 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);
scopeContexts.back()->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.get()));
}
// ECMA-262 13.12 The switch statement
template <class ASTBuilder>
ASTPassNode parseSwitchCase(ASTBuilder& builder, bool& isDefaultNode)
{
MetaNode node = this->createNode();
ASTNode test;
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).get());
}
return this->finalize(node, builder.createSwitchCaseNode(test.get(), consequent.get()));
}
template <class ASTBuilder>
ASTPassNode parseSwitchStatement(ASTBuilder& builder)
{
this->expectKeyword(SwitchKeyword);
this->expect(LeftParenthesis);
ASTNode discriminant;
discriminant = this->parseExpression(builder);
this->expect(RightParenthesis);
bool previousInSwitch = this->context->inSwitch;
this->context->inSwitch = true;
ASTStatementContainer casesA, casesB;
ASTNode deflt;
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.get());
} else {
casesB->appendChild(clause.get());
}
}
}
this->expect(RightBrace);
this->context->inSwitch = previousInSwitch;
return this->finalize(this->createNode(), builder.createSwitchStatementNode(discriminant.get(), casesA.get(), deflt.get(), casesB.get()));
}
// ECMA-262 13.13 Labelled Statements
template <class ASTBuilder>
ASTPassNode parseLabelledStatement(ASTBuilder& builder, size_t multiLabelCount = 1)
{
ASTNode expr;
AtomicString name;
bool isIdentifier = false;
expr = this->parseExpression(builder);
if (expr->type() == Identifier) {
isIdentifier = true;
name = expr->asIdentifier()->name();
}
ASTNodePtr statement;
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.get(), 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.get(), name.string());
}
removeLabel(name);
} else {
this->consumeSemicolon();
statement = builder.createExpressionStatementNode(expr.get());
}
return this->finalize(this->createNode(), statement);
}
// ECMA-262 13.14 The throw statement
template <class ASTBuilder>
ASTPassNode parseThrowStatement(ASTBuilder& builder)
{
auto metaNode = this->createNode();
this->expectKeyword(ThrowKeyword);
if (this->hasLineTerminator) {
this->throwError(Messages::NewlineAfterThrow);
}
ASTNode argument;
argument = this->parseExpression(builder);
this->consumeSemicolon();
return this->finalize(metaNode, builder.createThrowStatementNode(argument.get()));
}
// ECMA-262 13.15 The try statement
template <class ASTBuilder>
ASTPassNode parseCatchClause(ASTBuilder& builder)
{
this->expectKeyword(CatchKeyword);
this->expect(LeftParenthesis);
if (this->match(RightParenthesis)) {
this->throwUnexpectedToken(&this->lookahead);
}
ParserBlockContext catchBlockContext = openBlock();
ScannerResultVector 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;
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.get(), nullptr, body.get(), catchBlockContext.childLexicalBlockIndex));
}
template <class ASTBuilder>
ASTPassNode parseFinallyClause(ASTBuilder& builder)
{
this->expectKeyword(FinallyKeyword);
return this->parseBlock(builder);
}
template <class ASTBuilder>
ASTPassNode parseTryStatement(ASTBuilder& builder)
{
this->expectKeyword(TryKeyword);
ASTNode block = this->parseBlock(builder);
ASTNode handler;
if (this->matchKeyword(CatchKeyword)) {
handler = this->parseCatchClause(builder);
}
ASTNode finalizer;
if (this->matchKeyword(FinallyKeyword)) {
finalizer = this->parseFinallyClause(builder);
}
if (!handler && !finalizer) {
this->throwError(Messages::NoCatchOrFinally);
}
return this->finalize(this->createNode(), builder.createTryStatementNode(block.get(), handler.get(), finalizer.get()));
}
// 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>
ASTPassNode parseStatement(ASTBuilder& builder, bool allowFunctionDeclaration = true)
{
checkRecursiveLimit();
ASTNode statement;
switch (this->lookahead.type) {
case Token::BooleanLiteralToken:
case Token::NullLiteralToken:
case Token::NumericLiteralToken:
case Token::StringLiteralToken:
case Token::TemplateToken:
case Token::RegularExpressionToken:
statement = this->parseExpressionStatement(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;
}
PassRefPtr<StatementContainer> parseFunctionParameters()
{
RefPtr<StatementContainer> container = StatementContainer::create();
this->expect(LeftParenthesis);
ParseFormalParametersResult options;
size_t paramIndex = 0;
MetaNode node = this->createNode();
while (!this->match(RightParenthesis)) {
bool end = !this->parseFormalParameter(options);
RefPtr<Node> param = options.params.back();
switch (param->type()) {
case Identifier:
case AssignmentPattern:
case ArrayPattern:
case ObjectPattern: {
RefPtr<InitializeParameterExpressionNode> init = this->finalize(node, new InitializeParameterExpressionNode(param.get(), paramIndex));
RefPtr<Node> statement = this->finalize(node, new ExpressionStatementNode(init.get()));
container->appendChild(statement->asStatement());
break;
}
case RestElement: {
RefPtr<Node> statement = this->finalize(node, new ExpressionStatementNode(param.get()));
container->appendChild(statement->asStatement());
break;
}
default: {
ASSERT_NOT_REACHED();
break;
}
}
if (end) {
break;
}
paramIndex++;
this->expect(Comma);
}
this->expect(RightParenthesis);
return container;
}
PassRefPtr<BlockStatementNode> parseFunctionBody(NodeGenerator& builder)
{
// only for parsing the body of callee function
ASSERT(this->isParsingSingleFunction);
this->lexicalBlockIndex = 0;
this->lexicalBlockCount = 0;
this->subCodeBlockIndex = 0;
this->context->inCatchClause = false;
MetaNode nodeStart = this->createNode();
RefPtr<StatementContainer> body = StatementContainer::create();
this->expect(LeftBrace);
this->parseDirectivePrologues(body.get());
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).get(), referNode);
}
bool isEndedWithReturnNode = referNode && referNode->type() == ASTNodeType::ReturnStatement;
if (!isEndedWithReturnNode) {
referNode = body->appendChild(adoptRef(builder.createReturnStatementNode(nullptr)));
}
this->expect(RightBrace);
return this->finalize(nodeStart, builder.createBlockStatementNode(body.get(), 0));
}
// ECMA-262 14.1 Function Definition
template <class ASTBuilder>
ASTPassNode parseFunctionSourceElements(ASTBuilder& builder)
{
// return empty node because the function body node is never used now
ASTNode result;
if (this->isParsingSingleFunction) {
// when parsing for function call,
// parseFunctionSourceElements should parse only for child functions
ASSERT(this->childFunctionIndex > 0);
size_t realIndex = this->childFunctionIndex - 1;
this->childFunctionIndex++;
InterpretedCodeBlock* currentTarget = this->codeBlock->asInterpretedCodeBlock();
size_t orgIndex = this->lookahead.start;
this->expect(LeftBrace);
StringView src = currentTarget->childBlocks()[realIndex]->bodySrc();
this->scanner->index = src.length() + orgIndex;
this->scanner->lineNumber = currentTarget->childBlocks()[realIndex]->sourceElementStart().line;
this->scanner->lineStart = currentTarget->childBlocks()[realIndex]->sourceElementStart().index - currentTarget->childBlocks()[realIndex]->sourceElementStart().column;
this->lookahead.lineNumber = this->scanner->lineNumber;
this->lookahead.lineStart = this->scanner->lineStart;
this->lookahead.type = Token::PunctuatorToken;
this->lookahead.valuePunctuatorKind = PunctuatorKind::RightBrace;
this->expect(RightBrace);
//return this->finalize(this->createNode(), builder.createBlockStatementNode(StatementContainer::create().get(), this->lexicalBlockIndex));
return result;
}
ASSERT(!this->isParsingSingleFunction);
LexicalBlockIndex lexicalBlockIndexBefore = this->lexicalBlockIndex;
LexicalBlockIndex lexicalBlockCountBefore = this->lexicalBlockCount;
auto oldNameCallback = this->nameDeclaredCallback;
this->lexicalBlockIndex = 0;
this->lexicalBlockCount = 0;
size_t oldSubCodeBlockIndex = this->subCodeBlockIndex;
this->subCodeBlockIndex = 0;
bool oldInCatchClause = this->context->inCatchClause;
this->context->inCatchClause = false;
auto oldCatchClauseSimplyDeclaredVariableNames = std::move(this->context->catchClauseSimplyDeclaredVariableNames);
MetaNode nodeStart = this->createNode();
RefPtr<StatementContainer> body = StatementContainer::create();
this->expect(LeftBrace);
this->parseDirectivePrologues(body.get());
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;
// parseFunctionSourceElements only scans the function body
SyntaxChecker newBuilder;
while (this->startMarker.index < this->scanner->length) {
if (this->match(RightBrace)) {
break;
}
this->parseStatementListItem(newBuilder);
}
this->expect(RightBrace);
this->context->inCatchClause = oldInCatchClause;
this->context->catchClauseSimplyDeclaredVariableNames = std::move(oldCatchClauseSimplyDeclaredVariableNames);
this->lexicalBlockIndex = lexicalBlockIndexBefore;
this->lexicalBlockCount = lexicalBlockCountBefore;
this->nameDeclaredCallback = oldNameCallback;
this->subCodeBlockIndex = oldSubCodeBlockIndex;
this->context->labelSet = previousLabelSet;
this->context->inIteration = previousInIteration;
this->context->inSwitch = previousInSwitch;
this->context->inFunctionBody = previousInFunctionBody;
scopeContexts.back()->m_lexicalBlockIndexFunctionLocatedIn = lexicalBlockIndexBefore;
scopeContexts.back()->m_bodyStartLOC.line = nodeStart.line;
scopeContexts.back()->m_bodyStartLOC.column = nodeStart.column;
scopeContexts.back()->m_bodyStartLOC.index = nodeStart.index;
scopeContexts.back()->m_bodyEndLOC.index = this->lastMarker.index;
#ifndef NDEBUG
scopeContexts.back()->m_bodyEndLOC.line = this->lastMarker.lineNumber;
scopeContexts.back()->m_bodyEndLOC.column = this->lastMarker.index - this->lastMarker.lineStart;
#endif
//return this->finalize(nodeStart, builder.createBlockStatementNode(StatementContainer::create().get(), this->lexicalBlockIndex));
return result;
}
template <class ASTBuilder>
ASTPassNode 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;
Scanner::ScannerResult firstRestricted;
{
ALLOC_TOKEN(token);
*token = this->lookahead;
id = this->parseVariableIdentifier(builder);
if (this->context->strict) {
if (this->scanner->isRestrictedWord(token->relatedSource())) {
this->throwUnexpectedToken(token, Messages::StrictFunctionName);
}
} else {
if (this->scanner->isRestrictedWord(token->relatedSource())) {
firstRestricted = *token;
message = Messages::StrictFunctionName;
} else if (this->scanner->isStrictModeReservedWord(token->relatedSource())) {
firstRestricted = *token;
message = Messages::StrictReservedWord;
}
}
}
MetaNode paramsStart = this->createNode();
this->expect(LeftParenthesis);
AtomicString fnName = id ? id->asIdentifier()->name() : AtomicString();
ASSERT(id);
addDeclaredNameIntoContext(fnName, this->lexicalBlockIndex, KeywordKind::VarKeyword);
insertUsingName(fnName);
pushScopeContext(fnName);
scopeContexts.back()->m_paramsStartLOC.index = paramsStart.index;
scopeContexts.back()->m_paramsStartLOC.column = paramsStart.column;
scopeContexts.back()->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(&firstRestricted);
Scanner::ScannerResult stricted = formalParameters.stricted;
firstRestricted = formalParameters.firstRestricted;
if (formalParameters.message) {
message = formalParameters.message;
}
extractNamesFromFunctionParams(formalParameters);
bool previousStrict = this->context->strict;
ASTNode body = this->parseFunctionSourceElements(builder);
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;
scopeContexts.back()->m_nodeType = ASTNodeType::FunctionDeclaration;
scopeContexts.back()->m_isGenerator = isGenerator;
return this->finalize(node, builder.createFunctionDeclarationNode(popScopeContext(node), isGenerator, subCodeBlockIndex));
}
template <class ASTBuilder>
ASTPassNode 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;
Scanner::ScannerResult 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->throwUnexpectedToken(token, Messages::StrictFunctionName);
}
} else {
if (this->scanner->isRestrictedWord(token->relatedSource())) {
firstRestricted = *token;
message = Messages::StrictFunctionName;
} else if (this->scanner->isStrictModeReservedWord(token->relatedSource())) {
firstRestricted = *token;
message = Messages::StrictReservedWord;
}
}
}
MetaNode paramsStart = this->createNode();
this->expect(LeftParenthesis);
AtomicString fnName = id ? id->asIdentifier()->name() : AtomicString();
pushScopeContext(fnName);
if (id) {
scopeContexts.back()->insertVarName(fnName, 0, false);
scopeContexts.back()->insertUsingName(fnName, 0);
}
scopeContexts.back()->m_paramsStartLOC.index = paramsStart.index;
scopeContexts.back()->m_paramsStartLOC.column = paramsStart.column;
scopeContexts.back()->m_paramsStartLOC.line = paramsStart.line;
ParseFormalParametersResult formalParameters = this->parseFormalParameters(&firstRestricted);
Scanner::ScannerResult stricted = formalParameters.stricted;
firstRestricted = formalParameters.firstRestricted;
if (formalParameters.message) {
message = formalParameters.message;
}
extractNamesFromFunctionParams(formalParameters);
bool previousStrict = this->context->strict;
ASTNode body = this->parseFunctionSourceElements(builder);
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;
scopeContexts.back()->m_nodeType = ASTNodeType::FunctionExpression;
scopeContexts.back()->m_isGenerator = isGenerator;
return this->finalize(node, builder.createFunctionExpressionNode(popScopeContext(node), isGenerator, subCodeBlockIndex));
}
// ECMA-262 14.1.1 Directive Prologues
// FIXME parse Directive by NodeGenerator only
PassRefPtr<Node> parseDirective()
{
ALLOC_TOKEN(token);
*token = this->lookahead;
StringView directiveValue;
bool isLiteral = false;
NodeGenerator newBuilder;
MetaNode node = this->createNode();
RefPtr<Node> expr = this->parseExpression(newBuilder);
if (expr->type() == Literal) {
isLiteral = true;
directiveValue = token->valueStringLiteral();
}
this->consumeSemicolon();
if (isLiteral) {
return this->finalize(node, newBuilder.createDirectiveNode(expr.get(), directiveValue));
} else {
return this->finalize(node, newBuilder.createExpressionStatementNode(expr.get()));
}
}
void parseDirectivePrologues(StatementContainer* container)
{
ASSERT(container != nullptr);
Scanner::ScannerResult firstRestricted;
ALLOC_TOKEN(token);
while (true) {
*token = this->lookahead;
if (token->type != StringLiteralToken) {
break;
}
RefPtr<Node> statement = this->parseDirective();
container->appendChild(statement->asStatement());
if (statement->type() != Directive) {
break;
}
DirectiveNode* directive = (DirectiveNode*)statement.get();
if (!token->hasAllocatedString && directive->value().equals("use strict")) {
this->scopeContexts.back()->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>
ASTPassNode parseGetterMethod(ASTBuilder& builder)
{
MetaNode node = this->createNode();
this->expect(LeftParenthesis);
this->expect(RightParenthesis);
bool isGenerator = false;
ParseFormalParametersResult params;
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;
pushScopeContext(AtomicString());
extractNamesFromFunctionParams(params);
ASTNode method = this->parsePropertyMethod(builder, params);
this->context->allowYield = previousAllowYield;
this->context->inArrowFunction = previousInArrowFunction;
this->context->allowSuperProperty = previousAllowSuperProperty;
scopeContexts.back()->m_paramsStartLOC.index = node.index;
scopeContexts.back()->m_paramsStartLOC.column = node.column;
scopeContexts.back()->m_paramsStartLOC.line = node.line;
scopeContexts.back()->m_allowSuperProperty = true;
scopeContexts.back()->m_nodeType = ASTNodeType::FunctionExpression;
scopeContexts.back()->m_isGenerator = isGenerator;
return this->finalize(node, builder.createFunctionExpressionNode(popScopeContext(node), isGenerator, this->subCodeBlockIndex));
}
template <class ASTBuilder>
ASTPassNode parseSetterMethod(ASTBuilder& builder)
{
MetaNode node = this->createNode();
bool isGenerator = false;
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);
pushScopeContext(AtomicString());
ParseFormalParametersResult formalParameters = this->parseFormalParameters();
if (formalParameters.params.size() != 1) {
this->throwError(Messages::BadSetterArity);
} else if (formalParameters.params[0]->type() == ASTNodeType::RestElement) {
this->throwError(Messages::BadSetterRestParameter);
}
extractNamesFromFunctionParams(formalParameters);
ASTNode method = this->parsePropertyMethod(builder, formalParameters);
this->context->allowYield = previousAllowYield;
this->context->allowSuperProperty = previousAllowSuperProperty;
this->context->inArrowFunction = previousInArrowFunction;
scopeContexts.back()->m_paramsStartLOC.index = node.index;
scopeContexts.back()->m_paramsStartLOC.column = node.column;
scopeContexts.back()->m_paramsStartLOC.line = node.line;
scopeContexts.back()->m_allowSuperProperty = true;
scopeContexts.back()->m_nodeType = ASTNodeType::FunctionExpression;
scopeContexts.back()->m_isGenerator = isGenerator;
return this->finalize(node, builder.createFunctionExpressionNode(popScopeContext(node), isGenerator, this->subCodeBlockIndex));
}
template <class ASTBuilder>
ASTPassNode parseGeneratorMethod(ASTBuilder& builder)
{
MetaNode node = this->createNode();
bool isGenerator = true;
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);
pushScopeContext(AtomicString());
ParseFormalParametersResult formalParameters = this->parseFormalParameters();
extractNamesFromFunctionParams(formalParameters);
ASTNode method = this->parsePropertyMethod(builder, formalParameters);
this->context->allowYield = previousAllowYield;
this->context->allowSuperProperty = previousAllowSuperProperty;
this->context->inArrowFunction = previousInArrowFunction;
scopeContexts.back()->m_paramsStartLOC.index = node.index;
scopeContexts.back()->m_paramsStartLOC.column = node.column;
scopeContexts.back()->m_paramsStartLOC.line = node.line;
scopeContexts.back()->m_allowSuperProperty = true;
scopeContexts.back()->m_nodeType = ASTNodeType::FunctionExpression;
scopeContexts.back()->m_isGenerator = isGenerator;
return this->finalize(node, builder.createFunctionExpressionNode(popScopeContext(node), isGenerator, this->subCodeBlockIndex));
}
// 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>
ASTPassNode parseYieldExpression(ASTBuilder& builder)
{
if (this->context->inParameterParsing) {
this->throwError("Cannot use yield expression within parameters");
}
MetaNode node = this->createNode();
this->expectKeyword(YieldKeyword);
ASTNode exprNode;
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;
}
scopeContexts.back()->m_hasYield = true;
return this->finalize(node, builder.createYieldExpressionNode(exprNode, delegate));
}
// ECMA-262 14.5 Class Definitions
template <class ASTBuilder>
ASTPassNode 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;
ASTNode value;
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() == "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() == "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() == "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.get(), keyString, "constructor")) {
allowSuperCall = true;
}
}
value = this->parsePropertyMethodFunction(builder, allowSuperCall);
}
if (kind == ClassElementNode::Kind::None) {
this->throwUnexpectedToken(&this->lookahead);
}
if (isStatic && !computed && this->isPropertyKey(keyNode.get(), keyString, "prototype")) {
this->throwUnexpectedToken(token, Messages::StaticPrototype);
}
if (!isStatic && !computed && this->isPropertyKey(keyNode.get(), 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;
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.get(), value.get(), kind, computed, isStatic));
}
template <class ASTBuilder>
ASTPassNode parseClassBody(ASTBuilder& builder, bool hasSuperClass, MetaNode& endNode)
{
MetaNode node = this->createNode();
ASTNodeVector body;
ASTNode constructor;
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>
ASTPassNode classDeclaration(ASTBuilder& builder, bool identifierIsOptional)
{
bool previousStrict = this->context->strict;
this->context->strict = true;
MetaNode startNode = this->createNode();
this->expectKeyword(ClassKeyword);
ASTNode idNode;
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;
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>
ASTPassNode parseClassDeclaration(ASTBuilder& builder)
{
return classDeclaration<ASTBuilder, ClassDeclarationNode>(builder, false);
}
template <class ASTBuilder>
ASTPassNode parseClassExpression(ASTBuilder& builder)
{
return classDeclaration<ASTBuilder, ClassExpressionNode>(builder, true);
}
// ECMA-262 15.1 Scripts
// ECMA-262 15.2 Modules
PassRefPtr<ProgramNode> parseProgram(NodeGenerator& builder)
{
MetaNode startNode = this->createNode();
pushScopeContext(new ASTFunctionScopeContext(this->context->strict));
this->context->allowLexicalDeclaration = true;
RefPtr<StatementContainer> container = StatementContainer::create();
this->parseDirectivePrologues(container.get());
StatementNode* referNode = nullptr;
while (this->startMarker.index < this->scanner->length) {
referNode = container->appendChild(this->parseStatementListItem(builder).get(), referNode);
}
scopeContexts.back()->m_bodyStartLOC.line = startNode.line;
scopeContexts.back()->m_bodyStartLOC.column = startNode.column;
scopeContexts.back()->m_bodyStartLOC.index = startNode.index;
MetaNode endNode = this->createNode();
#ifndef NDEBUG
scopeContexts.back()->m_bodyEndLOC.line = endNode.line;
scopeContexts.back()->m_bodyEndLOC.column = endNode.column;
#endif
scopeContexts.back()->m_bodyEndLOC.index = endNode.index;
return this->finalize(startNode, new ProgramNode(container.get(), scopeContexts.back(), this->moduleData));
}
PassRefPtr<FunctionNode> parseScriptFunction(NodeGenerator& builder)
{
ASSERT(this->isParsingSingleFunction);
MetaNode node = this->createNode();
RefPtr<StatementContainer> params = this->parseFunctionParameters();
RefPtr<BlockStatementNode> body = this->parseFunctionBody(builder);
return this->finalize(node, new FunctionNode(params.get(), body.get()));
}
PassRefPtr<FunctionNode> parseScriptArrowFunction(NodeGenerator& builder, bool hasArrowParameterPlaceHolder)
{
ASSERT(this->isParsingSingleFunction);
MetaNode node = this->createNode();
RefPtr<StatementContainer> params;
RefPtr<BlockStatementNode> body;
// generate parameter statements
if (hasArrowParameterPlaceHolder) {
params = parseFunctionParameters();
} else {
RefPtr<Node> param = this->parseConditionalExpression(builder);
ASSERT(param->type() == Identifier);
params = StatementContainer::create();
RefPtr<InitializeParameterExpressionNode> init = this->finalize(node, new InitializeParameterExpressionNode(param.get(), 0));
RefPtr<Node> statement = this->finalize(node, new ExpressionStatementNode(init.get()));
params->appendChild(statement->asStatement());
}
this->expect(Arrow);
this->context->inArrowFunction = true;
// generate body statements
if (this->match(LeftBrace)) {
body = parseFunctionBody(builder);
} else {
MetaNode nodeStart = this->createNode();
RefPtr<StatementContainer> container = StatementContainer::create();
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;
RefPtr<Node> expr = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<NodeGenerator, false>);
container->appendChild(this->finalize(nodeStart, new ReturnStatementNode(expr.get())), 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, new BlockStatementNode(container.get()));
}
return this->finalize(node, new FunctionNode(params.get(), body.get()));
}
// ECMA-262 15.2.2 Imports
template <class ASTBuilder>
ASTPassNode 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()));
}
// import {<foo as bar>} ...;
template <class ASTBuilder>
ASTPassNode parseImportSpecifier(ASTBuilder& builder)
{
MetaNode node = this->createNode();
ASTNode local;
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>
ASTPassNode 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>
ASTPassNode 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>
ASTPassNode 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;
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 = ((ImportNamespaceSpecifierNode*)specifiers.back().get())->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 = ((ImportDefaultSpecifierNode*)specifiers.back().get())->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 = ((ImportNamespaceSpecifierNode*)specifiers.back().get())->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 = ((ImportSpecifierNode*)v[i].get())->imported()->name();
entry.m_localName = ((ImportSpecifierNode*)v[i].get())->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>
ASTPassNode parseExportSpecifier(ASTBuilder& builder)
{
MetaNode node = this->createNode();
ASTNode local;
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>
ASTPassNode 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;
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()->scopeContext()->m_functionName.string()->length() ? declaration->asFunctionExpression()->scopeContext()->m_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().get() ? 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;
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;
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;
}
};
RefPtr<ProgramNode> parseProgram(::Escargot::Context* ctx, StringView source, bool isModule, bool strictFromOutside, bool inWith, size_t stackRemain, bool allowSuperCallOutside, bool allowSuperPropertyOutside)
{
Parser parser(ctx, source, isModule, stackRemain);
NodeGenerator builder;
parser.context->strict = strictFromOutside;
parser.context->inWith = inWith;
parser.context->allowSuperCall = allowSuperCallOutside;
parser.context->allowSuperProperty = allowSuperPropertyOutside;
RefPtr<ProgramNode> nd = parser.parseProgram(builder);
return nd;
}
RefPtr<FunctionNode> parseSingleFunction(::Escargot::Context* ctx, InterpretedCodeBlock* codeBlock, ASTFunctionScopeContext*& scopeContext, size_t stackRemain)
{
Parser parser(ctx, codeBlock->src(), false, stackRemain, codeBlock->sourceElementStart());
NodeGenerator builder;
parser.trackUsingNames = false;
parser.context->allowLexicalDeclaration = true;
parser.context->allowSuperCall = true;
parser.context->allowSuperProperty = true;
parser.isParsingSingleFunction = true;
parser.codeBlock = codeBlock;
// when parsing for function call, childindex should be set to index 1
parser.childFunctionIndex = 1;
scopeContext = new ASTFunctionScopeContext(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