escargot/src/parser/esprima_cpp/esprima.cpp
Hyukwoo Park 2b145ef54c
Update coding style guide (#296)
* reformat all single logical expression based on the new rule

Signed-off-by: HyukWoo Park <hyukwoo.park@samsung.com>
2019-07-03 15:42:41 +09:00

6174 lines
234 KiB
C++

/*
Copyright JS Foundation and other contributors, https://js.foundation/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 2016-present Samsung Electronics Co., Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include "Escargot.h"
#include "esprima.h"
#include "interpreter/ByteCode.h"
#include "parser/ast/AST.h"
#include "parser/CodeBlock.h"
#include "parser/Lexer.h"
#include "wtfbridge.h"
#define ALLOC_TOKEN(tokenName) Scanner::ScannerResult* tokenName = ALLOCA(sizeof(Scanner::ScannerResult), Scanner::ScannerResult, ec);
using namespace JSC::Yarr;
using namespace Escargot::EscargotLexer;
namespace Escargot {
namespace esprima {
namespace Messages {
const char* UnexpectedToken = "Unexpected token %s";
const char* UnexpectedNumber = "Unexpected number";
const char* UnexpectedString = "Unexpected string";
const char* UnexpectedIdentifier = "Unexpected identifier";
const char* UnexpectedReserved = "Unexpected reserved word";
const char* UnexpectedTemplate = "Unexpected quasi %s";
const char* UnexpectedEOS = "Unexpected end of input";
const char* NewlineAfterThrow = "Illegal newline after throw";
const char* InvalidRegExp = "Invalid regular expression";
const char* InvalidLHSInAssignment = "Invalid left-hand side in assignment";
const char* InvalidLHSInForIn = "Invalid left-hand side in for-in";
const char* InvalidLHSInForLoop = "Invalid left-hand side in for-loop";
const char* MultipleDefaultsInSwitch = "More than one default clause in switch statement";
const char* NoCatchOrFinally = "Missing catch or finally after try";
const char* UnknownLabel = "Undefined label \'%s\'";
const char* Redeclaration = "%s \'%s\' has already been declared";
const char* IllegalContinue = "Illegal continue statement";
const char* IllegalBreak = "Illegal break statement";
const char* IllegalReturn = "Illegal return statement";
const char* StrictModeWith = "Strict mode code may not include a with statement";
const char* StrictCatchVariable = "Catch variable may not be eval or arguments in strict mode";
const char* StrictVarName = "Variable name may not be eval or arguments in strict mode";
const char* StrictParamName = "Parameter name eval or arguments is not allowed in strict mode";
const char* StrictParamDupe = "Strict mode function may not have duplicate parameter names";
const char* StrictFunctionName = "Function name may not be eval or arguments in strict mode";
const char* StrictOctalLiteral = "Octal literals are not allowed in strict mode.";
const char* StrictLeadingZeroLiteral = "Decimals with leading zeros are not allowed in strict mode.";
const char* StrictDelete = "Delete of an unqualified identifier in strict mode.";
const char* StrictLHSAssignment = "Assignment to eval or arguments is not allowed in strict mode";
const char* StrictLHSPostfix = "Postfix increment/decrement may not have eval or arguments operand in strict mode";
const char* StrictLHSPrefix = "Prefix increment/decrement may not have eval or arguments operand in strict mode";
const char* StrictReservedWord = "Use of future reserved word in strict mode";
const char* ParameterAfterRestParameter = "Rest parameter must be last formal parameter";
const char* DefaultRestParameter = "Unexpected token =";
const char* ObjectPatternAsRestParameter = "Unexpected token {";
const char* DuplicateProtoProperty = "Duplicate __proto__ fields are not allowed in object literals";
const char* ConstructorSpecialMethod = "Class constructor may not be an accessor";
const char* DuplicateConstructor = "A class may only have one constructor";
const char* StaticPrototype = "Classes may not have static property named prototype";
const char* MissingFromClause = "Unexpected token";
const char* NoAsAfterImportNamespace = "Unexpected token";
const char* InvalidModuleSpecifier = "Unexpected token";
const char* IllegalImportDeclaration = "Unexpected token";
const char* IllegalExportDeclaration = "Unexpected token";
const char* DuplicateBinding = "Duplicate binding %s";
const char* ForInOfLoopInitializer = "%s loop variable declaration may not have an initializer";
} // namespace Messages
struct Config {
// Config always allocated on the stack
MAKE_STACK_ALLOCATED();
bool range : 1;
bool loc : 1;
bool tokens : 1;
bool comment : 1;
bool reparseArguments : 1;
bool parseSingleFunction : 1;
uint32_t parseSingleFunctionChildIndex;
CodeBlock* parseSingleFunctionTarget;
};
struct Context {
// Escargot::esprima::Context always allocated on the stack
MAKE_STACK_ALLOCATED();
bool allowIn : 1;
bool allowYield : 1;
bool isAssignmentTarget : 1;
bool isBindingElement : 1;
bool inFunctionBody : 1;
bool inIteration : 1;
bool inSwitch : 1;
bool inCatch : 1;
bool inArrowFunction : 1;
bool inDirectCatchScope : 1;
bool inParsingDirective : 1;
bool inWith : 1;
bool inLoop : 1;
bool strict : 1;
Scanner::ScannerResult firstCoverInitializedNameError;
std::vector<std::pair<AtomicString, size_t>> labelSet; // <LabelString, with statement count>
std::vector<FunctionDeclarationNode*> functionDeclarationsInDirectCatchScope;
};
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;
};
#define Parse PassNode<Node>, true
#define ParseAs(nodeType) PassNode<nodeType>, true
#define Scan ScanExpressionResult, false
#define ScanAsVoid void, false
class Parser {
public:
// Parser always allocated on the stack
MAKE_STACK_ALLOCATED();
::Escargot::Context* escargotContext;
Config config;
Scanner* scanner;
Scanner scannerInstance;
/*
std::unordered_map<IdentifierNode*, RefPtr<Scanner::ScannerResult>,
std::hash<IdentifierNode*>, std::equal_to<IdentifierNode*>, GCUtil::gc_malloc_ignore_off_page_allocator<std::pair<IdentifierNode*, RefPtr<Scanner::ScannerResult>>>>
nodeExtraInfo;
*/
enum SourceType {
Script,
Module
};
SourceType sourceType;
Scanner::ScannerResult lookahead;
bool hasLineTerminator;
Context contextInstance;
Context* context;
Marker baseMarker;
Marker startMarker;
Marker lastMarker;
Vector<ASTScopeContext*, gc_allocator_ignore_off_page<ASTScopeContext*>> scopeContexts;
bool trackUsingNames;
AtomicString lastUsingName;
size_t stackLimit;
AtomicString lastScanIdentifierName;
class ScanExpressionResult;
/* Structure for conversion. Do not use it for storing a value! */
template <typename T>
class PassNode : public PassRefPtr<T> {
public:
PassNode(PassRefPtr<T> node)
: PassRefPtr<T>(node)
{
}
operator ScanExpressionResult()
{
return ScanExpressionResult();
}
};
class ScanExpressionResult {
public:
ScanExpressionResult(ASTNodeType nodeType)
: nodeType(nodeType)
{
}
ScanExpressionResult()
: nodeType(ASTNodeTypeError)
{
}
template <typename T>
ScanExpressionResult(PassRefPtr<T>)
: nodeType(ASTNodeTypeError)
{
}
template <typename T>
operator PassNode<T>()
{
return PassNode<T>(nullptr);
}
operator ASTNodeType()
{
return nodeType;
}
void setNodeType(ASTNodeType newNodeType)
{
nodeType = newNodeType;
}
private:
ASTNodeType nodeType;
};
ASTScopeContext fakeContext;
ASTScopeContext* popScopeContext(const MetaNode& node)
{
auto ret = scopeContexts.back();
scopeContexts.pop_back();
lastUsingName = AtomicString();
return ret;
}
void pushScopeContext(ASTScopeContext* ctx)
{
scopeContexts.push_back(ctx);
lastUsingName = AtomicString();
}
ALWAYS_INLINE void insertUsingName(AtomicString name)
{
if (lastUsingName == name) {
return;
}
scopeContexts.back()->insertUsingName(name);
lastUsingName = name;
}
void extractNamesFromFunctionParams(const PatternNodeVector& vector)
{
if (this->config.parseSingleFunction)
return;
scopeContexts.back()->m_parameters.resizeWithUninitializedValues(vector.size());
for (size_t i = 0; i < vector.size(); i++) {
AtomicString id;
if (vector[i]->isIdentifier()) {
id = vector[i]->asIdentifier()->name();
} else if (vector[i]->isDefaultArgument()) {
id = vector[i]->asDefaultArgument()->left()->asIdentifier()->name();
} else if (vector[i]->isPattern()) {
id = this->createPatternName(i);
} else {
ASSERT(vector[i]->type() == ASTNodeType::ArrowParameterPlaceHolder);
scopeContexts.back()->m_parameters.resize(scopeContexts.back()->m_parameters.size() - 1);
continue;
}
scopeContexts.back()->m_parameters[i] = id;
scopeContexts.back()->insertName(id, true);
}
}
void pushScopeContext(AtomicString functionName)
{
if (this->config.parseSingleFunction) {
fakeContext = ASTScopeContext();
pushScopeContext(&fakeContext);
return;
}
auto parentContext = scopeContexts.back();
pushScopeContext(new ASTScopeContext(this->context->strict));
scopeContexts.back()->m_functionName = functionName;
scopeContexts.back()->m_inCatch = this->context->inCatch;
scopeContexts.back()->m_inWith = this->context->inWith;
if (parentContext) {
parentContext->m_childScopes.push_back(scopeContexts.back());
}
}
Parser(::Escargot::Context* escargotContext, StringView code, 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;
trackUsingNames = true;
config.range = false;
config.loc = false;
// config.source = String::emptyString;
config.tokens = false;
config.comment = false;
config.parseSingleFunction = false;
config.parseSingleFunctionTarget = nullptr;
config.parseSingleFunctionChildIndex = 0;
/*
this->config = {
range: (typeof options.range == 'boolean') && options.range,
loc: (typeof options.loc == 'boolean') && options.loc,
source: null,
tokens: (typeof options.tokens == 'boolean') && options.tokens,
comment: (typeof options.comment == 'boolean') && options.comment,
};
if (this->config.loc && options.source && options.source !== null) {
this->config.source = String(options.source);
}*/
this->scanner = &scannerInstance;
if (stackRemain >= STACK_LIMIT_FROM_BASE) {
stackRemain = STACK_LIMIT_FROM_BASE;
}
// this->sourceType = (options && options.sourceType == 'module') ? 'module' : 'script';
this->sourceType = Script;
this->hasLineTerminator = false;
this->context = &contextInstance;
this->context->allowIn = true;
this->context->allowYield = true;
this->context->firstCoverInitializedNameError.reset();
this->context->isAssignmentTarget = true;
this->context->isBindingElement = true;
this->context->inFunctionBody = false;
this->context->inIteration = false;
this->context->inSwitch = false;
this->context->inCatch = false;
this->context->inArrowFunction = false;
this->context->inDirectCatchScope = false;
this->context->inParsingDirective = false;
this->context->inWith = false;
this->context->inLoop = 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;
}
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) ? token->valueTemplate->raw : 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();
}
bool shouldCreateAST()
{
return !inProgramParsingAndInFunctionSourceNode() || this->context->inParsingDirective;
}
bool inProgramParsingAndInFunctionSourceNode()
{
return !this->config.parseSingleFunction && this->scopeContexts.size() > 1;
}
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>
PassRefPtr<T> finalize(MetaNode meta, T* node)
{
/*
if (this->config.range) {
node.range = [meta.index, this->lastMarker.index];
}
if (this->config.loc) {
node.loc = {
start: {
line: meta.line,
column: meta.column
},
end: {
line: this->lastMarker.lineNumber,
column: this->lastMarker.index - this->lastMarker.lineStart
}
};
if (this->config.source) {
node.loc.source = this->config.source;
}
}*/
auto type = node->type();
if (type == CallExpression) {
CallExpressionNode* c = (CallExpressionNode*)node;
if (c->callee() && c->callee()->isIdentifier() && ((IdentifierNode*)c->callee())->name() == this->escargotContext->staticStrings().eval) {
scopeContexts.back()->m_hasEval = true;
if (this->context->inArrowFunction) {
insertUsingName(this->escargotContext->staticStrings().stringThis);
}
}
} else if (type == WithStatement) {
scopeContexts.back()->m_hasWith = true;
} else if (type == YieldExpression) {
scopeContexts.back()->m_hasYield = true;
} else if (type == CatchClause) {
scopeContexts.back()->m_hasCatch = true;
}
node->m_loc = NodeLOC(meta.index);
return adoptRef(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(KeywordKind keyword)
{
return this->lookahead.type == Token::IdentifierToken && this->lookahead.valueKeywordKind == keyword;
}
// Return true if the next token is an assignment operator
bool matchAssign()
{
if (this->lookahead.type != Token::PunctuatorToken) {
return false;
}
PunctuatorKind op = this->lookahead.valuePunctuatorKind;
if (op >= Substitution && op < SubstitutionEnd) {
return true;
}
return false;
}
// Cover grammar support.
//
// When an assignment expression position starts with an left parenthesis, the determination of the type
// of the syntax is to be deferred arbitrarily long until the end of the parentheses pair (plus a lookahead)
// or the first comma. This situation also defers the determination of all the expressions nested in the pair.
//
// There are three productions that can be parsed in a parentheses pair that needs to be determined
// after the outermost pair is closed. They are:
//
// 1. AssignmentExpression
// 2. BindingElements
// 3. AssignmentTargets
//
// In order to avoid exponential backtracking, we use two flags to denote if the production can be
// binding element or assignment target.
//
// The three productions have the relationship:
//
// BindingElements ⊆ AssignmentTargets ⊆ AssignmentExpression
//
// with a single exception that CoverInitializedName when used directly in an Expression, generates
// an early error. Therefore, we need the third state, firstCoverInitializedNameError, to track the
// first usage of CoverInitializedName and report it when we reached the end of the parentheses pair.
//
// isolateCoverGrammar function runs the given parser function with a new cover grammar context, and it does not
// effect the current flags. This means the production the parser parses is only used as an expression. Therefore
// the CoverInitializedName check is conducted.
//
// inheritCoverGrammar function runs the given parse function with a new cover grammar context, and it propagates
// the flags outside of the parser. This means the production the parser parses is used as a part of a potential
// pattern. The CoverInitializedName check is deferred.
typedef Node* (Parser::*ParseFunction)();
typedef Vector<Scanner::ScannerResult, GCUtil::gc_malloc_ignore_off_page_allocator<Scanner::ScannerResult>>& ParamScanList;
struct IsolateCoverGrammarContext {
bool previousIsBindingElement;
bool previousIsAssignmentTarget;
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 = this->context->firstCoverInitializedNameError;
this->context->isBindingElement = true;
this->context->isAssignmentTarget = true;
this->context->firstCoverInitializedNameError.reset();
checkRecursiveLimit();
}
void endIsolateCoverGrammar(IsolateCoverGrammarContext* grammarContext)
{
ASSERT(grammarContext != nullptr);
if (UNLIKELY(this->context->firstCoverInitializedNameError)) {
this->throwUnexpectedToken(&this->context->firstCoverInitializedNameError);
}
this->context->isBindingElement = grammarContext->previousIsBindingElement;
this->context->isAssignmentTarget = grammarContext->previousIsAssignmentTarget;
this->context->firstCoverInitializedNameError = grammarContext->previousFirstCoverInitializedNameError;
}
void endInheritCoverGrammar(IsolateCoverGrammarContext* grammarContext)
{
ASSERT(grammarContext != nullptr);
this->context->isBindingElement = this->context->isBindingElement && grammarContext->previousIsBindingElement;
this->context->isAssignmentTarget = this->context->isAssignmentTarget && grammarContext->previousIsAssignmentTarget;
if (UNLIKELY(grammarContext->previousFirstCoverInitializedNameError)) {
this->context->firstCoverInitializedNameError = grammarContext->previousFirstCoverInitializedNameError;
}
}
template <typename T>
ALWAYS_INLINE PassRefPtr<Node> isolateCoverGrammar(T parseFunction)
{
IsolateCoverGrammarContext grammarContext;
startCoverGrammar(&grammarContext);
PassRefPtr<Node> result = (this->*parseFunction)();
endIsolateCoverGrammar(&grammarContext);
return result;
}
template <typename T>
ALWAYS_INLINE ScanExpressionResult scanIsolateCoverGrammar(T parseFunction)
{
IsolateCoverGrammarContext grammarContext;
startCoverGrammar(&grammarContext);
ScanExpressionResult ret = (this->*parseFunction)();
endIsolateCoverGrammar(&grammarContext);
return ret;
}
template <typename T>
ALWAYS_INLINE PassRefPtr<Node> isolateCoverGrammarWithFunctor(T parseFunction)
{
IsolateCoverGrammarContext grammarContext;
startCoverGrammar(&grammarContext);
PassRefPtr<Node> result = parseFunction();
endIsolateCoverGrammar(&grammarContext);
return result;
}
template <typename T>
ALWAYS_INLINE void scanIsolateCoverGrammarWithFunctor(T parseFunction)
{
IsolateCoverGrammarContext grammarContext;
startCoverGrammar(&grammarContext);
parseFunction();
endIsolateCoverGrammar(&grammarContext);
}
template <typename T>
ALWAYS_INLINE PassRefPtr<Node> inheritCoverGrammar(T parseFunction)
{
IsolateCoverGrammarContext grammarContext;
startCoverGrammar(&grammarContext);
PassRefPtr<Node> result = (this->*parseFunction)();
endInheritCoverGrammar(&grammarContext);
return result;
}
template <typename T>
ALWAYS_INLINE ScanExpressionResult scanInheritCoverGrammar(T parseFunction)
{
IsolateCoverGrammarContext grammarContext;
startCoverGrammar(&grammarContext);
ScanExpressionResult result = (this->*parseFunction)();
endInheritCoverGrammar(&grammarContext);
return result;
}
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;
}
}
IdentifierNode* finishIdentifier(Scanner::ScannerResult* token, bool isScopeVariableName)
{
ASSERT(token != nullptr);
IdentifierNode* 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 = new IdentifierNode(this->escargotContext->staticStrings().asciiTable[firstCh]);
} else {
if (!token->plain) {
ret = new IdentifierNode(AtomicString(this->escargotContext, sv->string()));
} else {
ret = new IdentifierNode(AtomicString(this->escargotContext, SourceStringView(*sv)));
}
}
if (trackUsingNames) {
insertUsingName(ret->name());
}
return ret;
}
ScanExpressionResult finishScanIdentifier(Scanner::ScannerResult* token, bool isScopeVariableName)
{
ASSERT(token != nullptr);
StringView* sv = token->valueStringLiteral();
const auto& a = sv->bufferAccessData();
char16_t firstCh = a.charAt(0);
if (a.length == 1 && firstCh < ESCARGOT_ASCII_TABLE_MAX) {
lastScanIdentifierName = this->escargotContext->staticStrings().asciiTable[firstCh];
} else {
if (!token->plain) {
lastScanIdentifierName = AtomicString(this->escargotContext, sv->string());
} else {
lastScanIdentifierName = AtomicString(this->escargotContext, SourceStringView(*sv));
}
}
if (trackUsingNames) {
insertUsingName(lastScanIdentifierName);
}
return ScanExpressionResult(ASTNodeType::Identifier);
}
#define DEFINE_AS_NODE(TypeName) \
RefPtr<TypeName##Node> as##TypeName##Node(RefPtr<Node> n) \
{ \
if (!n) \
return RefPtr<TypeName##Node>(nullptr); \
ASSERT(n->is##TypeName##Node()); \
return RefPtr<TypeName##Node>((TypeName##Node*)n.get()); \
}
DEFINE_AS_NODE(Expression);
DEFINE_AS_NODE(Statement);
// ECMA-262 12.2 Primary Expressions
template <typename T, bool isParse>
T primaryExpression(void)
{
MetaNode node = this->createNode();
// let value, token, raw;
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);
if (isParse) {
return T(this->finalize(node, finishIdentifier(token, true)));
}
return finishScanIdentifier(token, true);
}
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));
if (isParse) {
return T(this->finalize(node, new LiteralNode(Value(token->valueNumber))));
}
return ScanExpressionResult(ASTNodeType::Literal);
} else {
if (isParse) {
if (shouldCreateAST()) {
return T(this->finalize(node, new LiteralNode(token->valueStringLiteralForAST())));
}
return T(this->finalize(node, new LiteralNode(Value(String::emptyString))));
}
return ScanExpressionResult(ASTNodeType::Literal);
}
}
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));
if (isParse) {
return T(this->finalize(node, new LiteralNode(Value(value))));
}
return ScanExpressionResult(ASTNodeType::Literal);
}
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));
if (isParse) {
return T(this->finalize(node, new LiteralNode(Value(Value::Null))));
}
return ScanExpressionResult(ASTNodeType::Literal);
}
case Token::TemplateToken:
if (isParse) {
return T(this->parseTemplateLiteral());
}
this->parseTemplateLiteral();
return ScanExpressionResult(ASTNodeType::TemplateLiteral);
case Token::PunctuatorToken: {
PunctuatorKind value = this->lookahead.valuePunctuatorKind;
switch (value) {
case LeftParenthesis:
this->context->isBindingElement = false;
if (isParse) {
return T(this->inheritCoverGrammar(&Parser::groupExpression<Parse>));
}
return this->scanInheritCoverGrammar(&Parser::groupExpression<Scan>);
case LeftSquareBracket:
if (isParse) {
return T(this->inheritCoverGrammar(&Parser::arrayInitializer<Parse>));
}
this->scanInheritCoverGrammar(&Parser::arrayInitializer<Scan>);
return ScanExpressionResult(ASTNodeType::ArrayExpression);
case LeftBrace:
if (isParse) {
return T(this->inheritCoverGrammar(&Parser::objectInitializer<Parse>));
}
this->scanInheritCoverGrammar(&Parser::objectInitializer<Scan>);
return ScanExpressionResult(ASTNodeType::ObjectExpression);
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);
if (isParse) {
return T(this->finalize(node, new RegExpLiteralNode(token->valueRegexp.body, token->valueRegexp.flags)));
}
this->finalize(node, new RegExpLiteralNode(token->valueRegexp.body, token->valueRegexp.flags));
return ScanExpressionResult(ASTNodeType::Literal);
}
default: {
ALLOC_TOKEN(token);
this->nextToken(token);
this->throwUnexpectedToken(token);
}
}
break;
}
case Token::KeywordToken:
if (!this->context->strict && this->context->allowYield && this->matchKeyword(YieldKeyword)) {
if (isParse) {
return T(this->parseIdentifierName());
}
return this->scanIdentifierName();
} 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)) {
if (isParse) {
return T(this->parseFunctionExpression());
}
this->parseFunctionExpression();
return ScanExpressionResult(ASTNodeType::FunctionExpression);
} else if (this->matchKeyword(ThisKeyword)) {
if (this->context->inArrowFunction) {
insertUsingName(this->escargotContext->staticStrings().stringThis);
}
this->nextToken();
if (isParse) {
return T(this->finalize(node, new ThisExpressionNode()));
}
return ScanExpressionResult(ASTNodeType::ThisExpression);
} else if (this->matchKeyword(SuperKeyword)) {
this->nextToken();
if (!this->match(LeftParenthesis) && !this->match(Period) && !this->match(LeftSquareBracket)) {
this->throwUnexpectedToken(&this->lookahead);
}
if (isParse) {
return T(this->finalize(node, new SuperExpressionNode(this->lookahead.valuePunctuatorKind == LeftParenthesis)));
}
return ScanExpressionResult(ASTNodeType::SuperExpression);
} else if (this->matchKeyword(ClassKeyword)) {
if (isParse) {
return T(this->parseClassExpression());
}
this->parseClassExpression();
return ScanExpressionResult(ASTNodeType::ASTNodeTypeError);
} else {
ALLOC_TOKEN(token);
this->nextToken(token);
this->throwUnexpectedToken(token);
}
}
break;
default: {
ALLOC_TOKEN(token);
this->nextToken(token);
this->throwUnexpectedToken(token);
}
}
ASSERT_NOT_REACHED();
if (isParse) {
return T(PassRefPtr<Node>());
}
return ScanExpressionResult();
}
struct ParseParameterOptions {
PatternNodeVector params;
std::vector<AtomicString, gc_allocator<AtomicString>> paramSet;
Scanner::ScannerResult stricted;
const char* message;
Scanner::ScannerResult firstRestricted;
ParseParameterOptions()
: message(nullptr)
{
}
};
void validateParam(ParseParameterOptions& 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);
if (scopeContexts.back()->m_hasRestElement || scopeContexts.back()->m_hasNonIdentArgument) {
for (size_t i = 0; i < options.paramSet.size() - 1; i++) {
// Check if any identifier names are duplicated.
// Note: using this inner for loop because std::find didn't work for some reason.
for (size_t j = i + 1; j < options.paramSet.size(); j++) {
if (options.paramSet[i] == options.paramSet[j]) {
this->throwError("duplicate argument names are not allowed in this context");
}
}
}
}
}
AtomicString createPatternName(size_t index)
{
ExecutionState stateForTest(this->escargotContext);
return AtomicString(this->escargotContext, Value(index).toString(stateForTest));
}
PassRefPtr<IdentifierNode> parseRestElement(ParamScanList params, PunctuatorKind endKind = RightParenthesis)
{
this->nextToken();
if (this->match(LeftBrace)) {
this->throwError(Messages::ObjectPatternAsRestParameter);
}
params.push_back(this->lookahead);
MetaNode node = this->createNode();
RefPtr<IdentifierNode> param = this->parseVariableIdentifier();
if (this->match(Equal)) {
this->throwError(Messages::DefaultRestParameter);
}
if (!this->match(endKind)) {
this->throwError(Messages::ParameterAfterRestParameter);
}
return this->finalize(node, new IdentifierNode(param.get()->name()));
}
template <typename T, bool isParse>
T arrayPattern(ParamScanList params, KeywordKind kind = KeywordKindEnd)
{
MetaNode node = this->createNode();
this->expect(LeftSquareBracket);
bool hasRestElement = false;
ExpressionNodeVector elements;
while (!this->match(RightSquareBracket)) {
if (this->match(Comma)) {
this->nextToken();
elements.push_back(nullptr);
} else {
if (this->match(PeriodPeriodPeriod)) {
elements.push_back(this->parseRestElement(params, RightSquareBracket));
hasRestElement = true;
break;
} else {
elements.push_back(this->parsePatternWithDefault(params, kind));
}
if (!this->match(RightSquareBracket)) {
this->expect(Comma);
}
}
}
this->expect(RightSquareBracket);
if (isParse) {
return T(this->finalize(node, new ArrayPatternNode(std::move(elements), nullptr, hasRestElement)));
}
return ScanExpressionResult(ASTNodeType::ArrayPattern);
}
template <typename T, bool isParse>
T propertyPattern(ParamScanList params, KeywordKind kind = KeywordKindEnd)
{
ALLOC_TOKEN(token);
*token = this->lookahead;
MetaNode node = this->createNode();
RefPtr<Node> keyNode; //'': Node.PropertyKey;
RefPtr<Node> valueNode; //: Node.PropertyValue;
ScanExpressionResult key;
String* keyString = String::emptyString;
bool computed = false;
bool method = false;
bool shorthand = false;
if (token->type == Token::IdentifierToken) {
ALLOC_TOKEN(keyToken);
*keyToken = *token;
if (isParse) {
keyNode = this->parseVariableIdentifier();
} else {
key.setNodeType(ASTNodeType::Identifier);
this->scanVariableIdentifier();
}
if (this->match(Substitution)) {
if (isParse) {
params.push_back(*keyToken);
}
shorthand = true;
this->nextToken();
if (isParse) {
RefPtr<Node> expr = this->assignmentExpression<Parse>();
valueNode = this->finalize(this->startNode(keyToken), new AssignmentExpressionSimpleNode(keyNode.get(), expr.get()));
} else {
this->assignmentExpression<Scan>();
}
} else if (!this->match(Colon)) {
if (isParse) {
params.push_back(*keyToken);
}
shorthand = true;
valueNode = keyNode;
} else {
this->expect(Colon);
if (isParse) {
valueNode = this->parsePatternWithDefault(params, kind);
} else {
this->parsePatternWithDefault(params, kind);
}
}
} else {
computed = this->match(LeftSquareBracket);
if (isParse) {
keyNode = this->parseObjectPropertyKey();
} else {
this->scanObjectPropertyKey();
}
this->expect(Colon);
if (isParse) {
valueNode = this->parsePatternWithDefault(params, kind);
} else {
this->parsePatternWithDefault(params, kind);
}
}
if (isParse) {
// TODO add shorthand
return T(this->finalize(node, new PropertyNode(keyNode.get(), valueNode.get(), PropertyNode::Kind::Init, computed)));
}
return ScanExpressionResult(ASTNodeType::ObjectPattern);
}
template <typename T, bool isParse>
T objectPattern(ParamScanList params, KeywordKind kind = KeywordKindEnd)
{
MetaNode node = this->createNode();
PropertiesNodeVector properties;
this->expect(LeftBrace);
while (!this->match(RightBrace)) {
if (isParse) {
properties.push_back(this->propertyPattern<ParseAs(PropertyNode)>(params, kind));
} else {
this->propertyPattern<Scan>(params, kind);
}
if (!this->match(RightBrace)) {
this->expect(Comma);
}
}
this->expect(RightBrace);
if (isParse) {
return T(this->finalize(node, new ObjectPatternNode(std::move(properties), nullptr)));
}
return ScanExpressionResult(ASTNodeType::ObjectPattern);
}
template <typename T, bool isParse>
T pattern(ParamScanList params, KeywordKind kind = KeywordKindEnd)
{
if (this->match(LeftSquareBracket)) {
scopeContexts.back()->m_hasNonIdentArgument = true;
if (isParse) {
return T(this->arrayPattern<ParseAs(ArrayPatternNode)>(params, kind));
}
return ScanExpressionResult(ASTNodeType::ArrayPattern);
} else if (this->match(LeftBrace)) {
scopeContexts.back()->m_hasNonIdentArgument = true;
if (isParse) {
return T(this->objectPattern<ParseAs(ObjectPatternNode)>(params, kind));
}
return ScanExpressionResult(ASTNodeType::ObjectPattern);
} else {
if (this->matchKeyword(LetKeyword) && (kind == ConstKeyword || kind == LetKeyword)) {
this->throwUnexpectedToken(&this->lookahead, Messages::UnexpectedToken);
}
params.push_back(this->lookahead);
if (isParse) {
return T(this->parseVariableIdentifier(kind));
}
this->scanVariableIdentifier(kind);
return ScanExpressionResult(ASTNodeType::Identifier);
}
}
PassRefPtr<Node> parsePatternWithDefault(ParamScanList params, KeywordKind kind = KeywordKindEnd)
{
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
PassRefPtr<Node> pattern = this->pattern<Parse>(params, kind);
if (this->match(PunctuatorKind::Substitution)) {
scopeContexts.back()->m_hasNonIdentArgument = true;
this->nextToken();
const bool previousAllowYield = this->context->allowYield;
this->context->allowYield = true;
PassRefPtr<Node> right = this->isolateCoverGrammar(&Parser::assignmentExpression<Parse>);
this->context->allowYield = previousAllowYield;
if (pattern->isPattern()) {
pattern->asPattern()->setInitializer(right);
return pattern;
}
return this->finalize(this->startNode(startToken), new DefaultArgumentNode(pattern.get(), right.get()));
}
return pattern;
}
bool parseFormalParameter(ParseParameterOptions& options)
{
RefPtr<Node> param;
bool trackUsingNamesBefore = trackUsingNames;
trackUsingNames = false;
Vector<Scanner::ScannerResult, GCUtil::gc_malloc_ignore_off_page_allocator<Scanner::ScannerResult>> params;
ALLOC_TOKEN(token);
*token = this->lookahead;
if (token->type == Token::PunctuatorToken && token->valuePunctuatorKind == PunctuatorKind::PeriodPeriodPeriod) {
RefPtr<IdentifierNode> param = this->parseRestElement(params);
scopeContexts.back()->m_hasRestElement = true;
this->validateParam(options, &params.back(), param.get()->name());
options.params.push_back(param);
trackUsingNames = true;
return false;
}
param = this->parsePatternWithDefault(params);
for (size_t i = 0; i < params.size(); i++) {
AtomicString as(this->escargotContext, params[i].relatedSource());
this->validateParam(options, &params[i], as);
}
options.params.push_back(param);
trackUsingNames = trackUsingNamesBefore;
return !this->match(PunctuatorKind::RightParenthesis);
}
struct ParseFormalParametersResult {
PatternNodeVector params;
Scanner::ScannerResult stricted;
Scanner::ScannerResult firstRestricted;
const char* message;
bool valid;
ParseFormalParametersResult()
: message(nullptr)
, valid(false)
{
}
ParseFormalParametersResult(PatternNodeVector params, const char* message)
: params(std::move(params))
, message(nullptr)
, valid(true)
{
}
ParseFormalParametersResult(PatternNodeVector params, Scanner::ScannerResult& stricted, Scanner::ScannerResult& firstRestricted, const char* message)
: params(std::move(params))
, stricted(stricted)
, firstRestricted(firstRestricted)
, message(nullptr)
, valid(true)
{
}
};
ParseFormalParametersResult parseFormalParameters(Scanner::ScannerResult* firstRestricted = nullptr)
{
ParseParameterOptions options;
if (firstRestricted) {
options.firstRestricted = *firstRestricted;
}
if (!this->match(RightParenthesis)) {
options.paramSet.clear();
while (this->startMarker.index < this->scanner->length) {
if (!this->parseFormalParameter(options)) {
break;
}
this->expect(Comma);
}
}
this->expect(RightParenthesis);
return ParseFormalParametersResult(options.params, options.stricted, options.firstRestricted, options.message);
}
// ECMA-262 12.2.5 Array Initializer
template <typename T, bool isParse>
T spreadElement()
{
this->expect(PunctuatorKind::PeriodPeriodPeriod);
MetaNode node = this->createNode();
RefPtr<Node> arg = this->inheritCoverGrammar(&Parser::assignmentExpression<Parse>);
if (isParse) {
return T(this->finalize(node, new SpreadElementNode(arg.get())));
}
return ScanExpressionResult(ASTNodeType::SpreadElement);
}
template <typename T, bool isParse>
T arrayInitializer()
{
// const elements: Node.ArrayExpressionElement[] = [];
ExpressionNodeVector elements;
this->expect(LeftSquareBracket);
bool hasSpreadElement = false;
while (!this->match(RightSquareBracket)) {
if (this->match(Comma)) {
this->nextToken();
if (isParse) {
elements.push_back(nullptr);
}
} else if (this->match(PeriodPeriodPeriod)) {
if (isParse) {
elements.push_back(this->spreadElement<Parse>());
} else {
this->spreadElement<Scan>();
}
hasSpreadElement = true;
if (!this->match(RightSquareBracket)) {
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
this->expect(Comma);
}
} else {
if (isParse) {
elements.push_back(this->inheritCoverGrammar(&Parser::assignmentExpression<Parse>));
} else {
this->scanInheritCoverGrammar(&Parser::assignmentExpression<Scan>);
}
if (!this->match(RightSquareBracket)) {
this->expect(Comma);
}
}
}
this->expect(RightSquareBracket);
if (isParse) {
MetaNode node = this->createNode();
return T(this->finalize(node, new ArrayExpressionNode(std::move(elements), AtomicString(), nullptr, hasSpreadElement)));
}
return ScanExpressionResult(ASTNodeType::ArrayExpression);
}
// ECMA-262 12.2.6 Object Initializer
PassRefPtr<Node> parsePropertyMethod(ParseFormalParametersResult& params)
{
bool previousInArrowFunction = this->context->inArrowFunction;
this->context->inArrowFunction = false;
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
pushScopeContext(AtomicString());
extractNamesFromFunctionParams(params.params);
const bool previousStrict = this->context->strict;
PassRefPtr<Node> body = this->isolateCoverGrammar(&Parser::parseFunctionSourceElements);
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;
this->context->inArrowFunction = previousInArrowFunction;
return body;
}
PassRefPtr<FunctionExpressionNode> parsePropertyMethodFunction()
{
const bool isGenerator = false;
const bool previousAllowYield = this->context->allowYield;
this->context->allowYield = false;
MetaNode node = this->createNode();
this->expect(LeftParenthesis);
ParseFormalParametersResult params = this->parseFormalParameters();
RefPtr<Node> method = this->parsePropertyMethod(params);
this->context->allowYield = previousAllowYield;
scopeContexts.back()->m_paramsStart.index = node.index;
return this->finalize(node, new FunctionExpressionNode(AtomicString(), std::move(params.params), method.get(), popScopeContext(node), isGenerator));
}
PassRefPtr<Node> parseObjectPropertyKey()
{
ALLOC_TOKEN(token);
this->nextToken(token);
MetaNode node = this->createNode();
RefPtr<Node> 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 {
v = Value(token->valueStringLiteralForAST());
}
key = this->finalize(node, new LiteralNode(v));
}
break;
case Token::IdentifierToken:
case Token::BooleanLiteralToken:
case Token::NullLiteralToken:
case Token::KeywordToken: {
bool trackUsingNamesBefore = this->trackUsingNames;
this->trackUsingNames = false;
key = this->finalize(node, finishIdentifier(token, false));
this->trackUsingNames = trackUsingNamesBefore;
break;
}
case Token::PunctuatorToken:
if (token->valuePunctuatorKind == LeftSquareBracket) {
key = this->isolateCoverGrammar(&Parser::assignmentExpression<Parse>);
this->expect(RightSquareBracket);
} else {
this->throwUnexpectedToken(token);
}
break;
default:
this->throwUnexpectedToken(token);
}
return key;
}
std::pair<ScanExpressionResult, bool> scanObjectPropertyKey()
{
ALLOC_TOKEN(token);
this->nextToken(token);
ScanExpressionResult key(ASTNodeType::ASTNodeTypeError);
bool isProto = false;
switch (token->type) {
case Token::NumericLiteralToken:
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);
if (this->context->inLoop || token->valueNumber == 0)
this->scopeContexts.back()->insertNumeralLiteral(Value(token->valueNumber));
key.setNodeType(Literal);
break;
case Token::StringLiteralToken:
isProto = (*token->valueStringLiteral() == "__proto__");
key.setNodeType(Literal);
break;
case Token::IdentifierToken:
case Token::BooleanLiteralToken:
case Token::NullLiteralToken:
case Token::KeywordToken: {
bool trackUsingNamesBefore = this->trackUsingNames;
this->trackUsingNames = false;
key = finishScanIdentifier(token, false);
this->trackUsingNames = trackUsingNamesBefore;
break;
}
case Token::PunctuatorToken:
if (token->valuePunctuatorKind == LeftSquareBracket) {
key = this->scanIsolateCoverGrammar(&Parser::assignmentExpression<Scan>);
this->expect(RightSquareBracket);
} else {
this->throwUnexpectedToken(token);
}
break;
default:
this->throwUnexpectedToken(token);
}
return std::make_pair(key, isProto);
}
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 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;
}
template <typename T, bool isParse>
T objectProperty(bool& hasProto, std::vector<std::pair<AtomicString, size_t>>& usedNames) //: Node.Property
{
ALLOC_TOKEN(token);
*token = this->lookahead;
MetaNode node = this->createNode();
PropertyNode::Kind kind;
RefPtr<Node> keyNode; //'': Node.PropertyKey;
RefPtr<Node> valueNode; //: Node.PropertyValue;
ScanExpressionResult key;
AtomicString scanIdentifierName;
bool computed = false;
bool method = false;
bool shorthand = false;
bool isProto = false;
if (token->type == Token::IdentifierToken) {
this->nextToken();
if (isParse) {
keyNode = this->finalize(this->createNode(), finishIdentifier(token, true));
} else {
key = finishScanIdentifier(token, true);
scanIdentifierName = lastScanIdentifierName;
}
} else if (this->match(PunctuatorKind::Multiply)) {
this->nextToken();
} else {
computed = this->match(LeftSquareBracket);
if (isParse) {
keyNode = this->parseObjectPropertyKey();
} else {
auto keyValue = this->scanObjectPropertyKey();
key = keyValue.first;
isProto = keyValue.second;
scanIdentifierName = lastScanIdentifierName;
}
}
bool lookaheadPropertyKey = this->qualifiedPropertyName(&this->lookahead);
bool isGet = false;
bool isSet = 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);
if (isParse) {
keyNode = this->parseObjectPropertyKey();
this->context->allowYield = false;
valueNode = this->parseGetterMethod();
} else {
auto keyValue = this->scanObjectPropertyKey();
key = keyValue.first;
isProto = keyValue.second;
scanIdentifierName = lastScanIdentifierName;
this->context->allowYield = false;
this->parseGetterMethod();
}
} else if (isSet) {
kind = PropertyNode::Kind::Set;
computed = this->match(LeftSquareBracket);
if (isParse) {
keyNode = this->parseObjectPropertyKey();
valueNode = this->parseSetterMethod();
} else {
auto keyValue = this->scanObjectPropertyKey();
key = keyValue.first;
isProto = keyValue.second;
scanIdentifierName = lastScanIdentifierName;
this->parseSetterMethod();
}
} else if (token->type == Token::PunctuatorToken && token->valuePunctuatorKind == PunctuatorKind::Multiply && lookaheadPropertyKey) {
kind = PropertyNode::Kind::Init;
computed = this->match(LeftSquareBracket);
if (isParse) {
keyNode = this->parseObjectPropertyKey();
valueNode = this->parseGeneratorMethod();
} else {
auto keyValue = this->scanObjectPropertyKey();
key = keyValue.first;
scanIdentifierName = lastScanIdentifierName;
this->parseGeneratorMethod();
}
method = true;
} else {
if (isParse) {
if (!keyNode) {
this->throwUnexpectedToken(&this->lookahead);
}
} else {
if (key == ASTNodeType::ASTNodeTypeError) {
this->throwUnexpectedToken(&this->lookahead);
}
}
kind = PropertyNode::Kind::Init;
if (this->match(PunctuatorKind::Colon)) {
if (isParse) {
isProto = !this->config.parseSingleFunction && this->isPropertyKey(keyNode.get(), "__proto__");
} else {
isProto |= (key == ASTNodeType::Identifier && scanIdentifierName == this->escargotContext->staticStrings().__proto__);
}
if (!computed && isProto) {
if (hasProto) {
this->throwError(Messages::DuplicateProtoProperty);
}
hasProto = true;
}
this->nextToken();
if (isParse) {
valueNode = this->inheritCoverGrammar(&Parser::assignmentExpression<Parse>);
} else {
this->scanInheritCoverGrammar(&Parser::assignmentExpression<Scan>);
}
} else if (this->match(LeftParenthesis)) {
if (isParse) {
valueNode = this->parsePropertyMethodFunction();
} else {
this->parsePropertyMethodFunction();
}
method = true;
} else if (token->type == Token::IdentifierToken) {
#if ESCARGOT_ENABLE_ES2015 == 0
this->throwUnexpectedToken(token);
#endif
RefPtr<Node> id;
if (isParse) {
id = this->finalize(node, finishIdentifier(token, true));
}
if (this->match(Substitution)) {
this->context->firstCoverInitializedNameError = this->lookahead;
this->nextToken();
shorthand = true;
if (isParse) {
RefPtr<Node> init = this->isolateCoverGrammar(&Parser::assignmentExpression<Parse>);
valueNode = this->finalize(node, new AssignmentExpressionSimpleNode(id.get(), init.get()));
} else {
this->scanIsolateCoverGrammar(&Parser::assignmentExpression<Scan>);
}
} else {
shorthand = true;
if (isParse) {
valueNode = id;
}
}
} else {
ALLOC_TOKEN(token);
this->nextToken(token);
this->throwUnexpectedToken(token);
}
}
if (!this->config.parseSingleFunction && (isParse ? keyNode->isIdentifier() : key == ASTNodeType::Identifier)) {
AtomicString as = isParse ? keyNode->asIdentifier()->name() : scanIdentifierName;
bool seenInit = kind == PropertyNode::Kind::Init;
bool seenGet = kind == PropertyNode::Kind::Get;
bool seenSet = kind == PropertyNode::Kind::Set;
size_t len = usedNames.size();
for (size_t i = 0; i < len; i++) {
const auto& n = usedNames[i];
if (n.first == as) {
if (n.second == PropertyNode::Kind::Init) {
if (this->context->strict) {
if (seenInit || seenGet || seenSet) {
this->throwError("invalid object literal");
}
} else {
if (seenGet || seenSet) {
this->throwError("invalid object literal");
}
}
seenInit = true;
} else if (n.second == PropertyNode::Kind::Get) {
if (seenInit || seenGet) {
this->throwError("invalid object literal");
}
seenGet = true;
} else if (n.second == PropertyNode::Kind::Set) {
if (seenInit || seenSet) {
this->throwError("invalid object literal");
}
seenSet = true;
}
}
}
usedNames.push_back(std::make_pair(as, kind));
}
if (isParse) {
// return this->finalize(node, new PropertyNode(kind, key, computed, value, method, shorthand));
return T(this->finalize(node, new PropertyNode(keyNode.get(), valueNode.get(), kind, computed)));
}
return ScanExpressionResult();
}
template <typename T, bool isParse>
T objectInitializer()
{
this->expect(LeftBrace);
MetaNode node = this->createNode();
PropertiesNodeVector properties;
bool hasProto = false;
std::vector<std::pair<AtomicString, size_t>> usedNames;
while (!this->match(RightBrace)) {
if (isParse) {
properties.push_back(this->objectProperty<ParseAs(PropertyNode)>(hasProto, usedNames));
} else {
this->objectProperty<Scan>(hasProto, usedNames);
}
if (!this->match(RightBrace)) {
this->expectCommaSeparator();
}
}
this->expect(RightBrace);
if (isParse) {
return T(this->finalize(node, new ObjectExpressionNode(std::move(properties))));
}
return ScanExpressionResult(ASTNodeType::ObjectExpression);
}
// ECMA-262 12.2.9 Template Literals
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->raw = token->valueTemplate->raw;
tm->tail = token->valueTemplate->tail;
return tm;
}
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->raw = token->valueTemplate->raw;
tm->tail = token->valueTemplate->tail;
return tm;
}
PassRefPtr<Node> parseTemplateLiteral()
{
MetaNode node = this->createNode();
ExpressionNodeVector expressions;
TemplateElementVector* quasis = new (GC) TemplateElementVector;
quasis->push_back(this->parseTemplateHead());
while (!quasis->back()->tail) {
expressions.push_back(this->expression<Parse>());
TemplateElement* quasi = this->parseTemplateElement();
quasis->push_back(quasi);
}
return this->finalize(node, new TemplateLiteralNode(quasis, std::move(expressions)));
}
/*
// ECMA-262 12.2.10 The Grouping Operator
reinterpretExpressionAsPattern(expr) {
switch (expr.type) {
case Syntax.Identifier:
case Syntax.MemberExpression:
case Syntax.RestElement:
case Syntax.AssignmentPattern:
break;
case Syntax.SpreadElement:
expr.type = Syntax.RestElement;
this->reinterpretExpressionAsPattern(expr.argument);
break;
case Syntax.ArrayExpression:
expr.type = Syntax.ArrayPattern;
for (let i = 0; i < expr.elements.length; i++) {
if (expr.elements[i] !== null) {
this->reinterpretExpressionAsPattern(expr.elements[i]);
}
}
break;
case Syntax.ObjectExpression:
expr.type = Syntax.ObjectPattern;
for (let i = 0; i < expr.properties.length; i++) {
this->reinterpretExpressionAsPattern(expr.properties[i].value);
}
break;
case Syntax.AssignmentExpression:
expr.type = Syntax.AssignmentPattern;
delete expr.operator;
this->reinterpretExpressionAsPattern(expr.left);
break;
default:
// Allow other node type for tolerant parsing.
break;
}
}
parseGroupExpression(): ArrowParameterPlaceHolderNode | Node.Expression {
let expr;
this->expect('(');
if (this->match(')')) {
this->nextToken();
if (!this->match('=>')) {
this->expect('=>');
}
expr = {
type: ArrowParameterPlaceHolder,
params: []
};
} else {
const startToken = this->lookahead;
let params = [];
if (this->match('...')) {
expr = this->parseRestElement(params);
this->expect(')');
if (!this->match('=>')) {
this->expect('=>');
}
expr = {
type: ArrowParameterPlaceHolder,
params: [expr]
};
} else {
let arrow = false;
this->context.isBindingElement = true;
expr = this->inheritCoverGrammar(this->assignmentExpression<Parse>);
if (this->match(',')) {
const expressions = [];
this->context.isAssignmentTarget = false;
expressions.push(expr);
while (this->startMarker.index < this->scanner.length) {
if (!this->match(',')) {
break;
}
this->nextToken();
if (this->match('...')) {
if (!this->context.isBindingElement) {
this->throwUnexpectedToken(this->lookahead);
}
expressions.push(this->parseRestElement(params));
this->expect(')');
if (!this->match('=>')) {
this->expect('=>');
}
this->context.isBindingElement = false;
for (let i = 0; i < expressions.length; i++) {
this->reinterpretExpressionAsPattern(expressions[i]);
}
arrow = true;
expr = {
type: ArrowParameterPlaceHolder,
params: expressions
};
} else {
expressions.push(this->inheritCoverGrammar(this->assignmentExpression<Parse>));
}
if (arrow) {
break;
}
}
if (!arrow) {
expr = this->finalize(this->startNode(startToken), new Node.SequenceExpression(expressions));
}
}
if (!arrow) {
this->expect(')');
if (this->match('=>')) {
if (expr.type === Syntax.Identifier && expr.name === 'yield') {
arrow = true;
expr = {
type: ArrowParameterPlaceHolder,
params: [expr]
};
}
if (!arrow) {
if (!this->context.isBindingElement) {
this->throwUnexpectedToken(this->lookahead);
}
if (expr.type === Syntax.SequenceExpression) {
for (let i = 0; i < expr.expressions.length; i++) {
this->reinterpretExpressionAsPattern(expr.expressions[i]);
}
} else {
this->reinterpretExpressionAsPattern(expr);
}
const params = (expr.type === Syntax.SequenceExpression ? expr.expressions : [expr]);
expr = {
type: ArrowParameterPlaceHolder,
params: params
};
}
}
this->context.isBindingElement = false;
}
}
}
return expr;
}
*/
void reinterpretExpressionAsPattern(Node* expr, ASTNodeType parent = ASTNodeTypeError)
{
switch (expr->type()) {
case ArrayExpression: {
ArrayExpressionNode* array = expr->asArrayExpression();
array->setAsPattern();
ExpressionNodeVector& elements = array->elements();
for (size_t i = 0; i < elements.size(); i++) {
if (elements[i] != nullptr) {
this->reinterpretExpressionAsPattern(elements[i].get(), ArrayPattern);
}
}
break;
}
case ObjectExpression: {
ObjectExpressionNode* object = expr->asObjectExpression();
object->setAsPattern();
PropertiesNodeVector& properties = object->properties();
for (size_t i = 0; i < properties.size(); i++) {
if (properties[i] != nullptr) {
this->reinterpretExpressionAsPattern(properties[i].get(), ObjectPattern);
}
}
break;
}
default:
if (!expr->isValidAssignmentTarget()) {
if (parent == ObjectPattern && expr->type() != Property) {
this->throwError("Invalid destructuring assignment target");
}
if (parent == ObjectPattern) {
scopeContexts.back()->insertUsingName(expr->asProperty()->key()->asIdentifier()->name());
}
}
break;
}
}
void scanReinterpretExpressionAsPattern(ScanExpressionResult expr)
{
switch (expr) {
case ArrayExpression:
expr.setNodeType(ASTNodeType::ArrayPattern);
break;
case ObjectExpression:
expr.setNodeType(ASTNodeType::ObjectPattern);
break;
default:
break;
}
}
template <typename T, bool isParse>
T groupExpression()
{
this->expect(LeftParenthesis);
if (this->match(RightParenthesis)) {
this->nextToken();
if (!this->match(Arrow)) {
this->expect(Arrow);
}
//TODO
if (isParse) {
return T(this->finalize(this->startNode(&this->lookahead), new ArrowParameterPlaceHolderNode()));
}
return ScanExpressionResult(ASTNodeType::ArrowParameterPlaceHolder);
}
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
this->context->isBindingElement = true;
RefPtr<Node> exprNode;
ScanExpressionResult expr;
if (isParse) {
exprNode = this->inheritCoverGrammar(&Parser::assignmentExpression<Parse>);
} else {
expr = this->scanInheritCoverGrammar(&Parser::assignmentExpression<Scan>);
}
if (this->match(Comma)) {
ExpressionNodeVector expressions;
this->context->isAssignmentTarget = false;
if (isParse) {
expressions.push_back(exprNode);
}
while (this->startMarker.index < this->scanner->length) {
if (!this->match(Comma)) {
break;
}
this->nextToken();
if (isParse) {
expressions.push_back(this->inheritCoverGrammar(&Parser::assignmentExpression<Parse>));
} else {
this->scanInheritCoverGrammar(&Parser::assignmentExpression<Scan>);
}
}
if (isParse) {
exprNode = this->finalize(this->startNode(startToken), new SequenceExpressionNode(std::move(expressions)));
} else {
expr.setNodeType(ASTNodeType::SequenceExpression);
}
}
this->expect(RightParenthesis);
//TODO Scanning has not been implemented yet
if (this->match(Arrow) && isParse) {
bool arrow = false;
if (exprNode->type() == Identifier && exprNode->asIdentifier()->name() == "yield") {
this->throwError("Yield property is not supported yet");
RELEASE_ASSERT_NOT_REACHED();
/*
arrow = true;
expr = this->finalize(this->startNode(startToken), new ArrowParameterPlaceHolderNode());
expr = {
type: ArrowParameterPlaceHolder,
params: [expr],
};
*/
}
if (!arrow) {
if (!this->context->isBindingElement) {
this->throwUnexpectedToken(&this->lookahead);
}
if (exprNode->type() == SequenceExpression) {
const ExpressionNodeVector& expressions = ((SequenceExpressionNode*)exprNode.get())->expressions();
for (size_t i = 0; i < expressions.size(); i++) {
this->reinterpretExpressionAsPattern(expressions[i].get());
}
} else {
this->reinterpretExpressionAsPattern(exprNode.get());
}
//TODO
ExpressionNodeVector params;
if (exprNode->type() == SequenceExpression) {
params = ((SequenceExpressionNode*)exprNode.get())->expressions();
} else {
params.push_back(exprNode);
}
exprNode = this->finalize(this->startNode(&this->lookahead), new ArrowParameterPlaceHolderNode(std::move(params)));
}
this->context->isBindingElement = false;
}
if (isParse) {
return T(exprNode.release());
}
return expr;
}
// ECMA-262 12.3 Left-Hand-Side Expressions
ArgumentVector parseArguments()
{
this->expect(LeftParenthesis);
ArgumentVector args;
if (!this->match(RightParenthesis)) {
while (true) {
RefPtr<Node> expr;
if (this->match(PeriodPeriodPeriod)) {
expr = this->spreadElement<Parse>();
} else {
expr = this->isolateCoverGrammar(&Parser::assignmentExpression<Parse>);
}
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;
}
void scanArguments()
{
this->expect(LeftParenthesis);
ArgumentVector args;
size_t count = 0;
if (!this->match(RightParenthesis)) {
while (true) {
if (this->match(PeriodPeriodPeriod)) {
this->spreadElement<Parse>();
} else {
this->scanIsolateCoverGrammar(&Parser::assignmentExpression<Scan>);
}
count++;
if (this->match(RightParenthesis)) {
break;
}
this->expectCommaSeparator();
}
}
this->expect(RightParenthesis);
if (UNLIKELY(count > 65535)) {
this->throwError("too many arguments in call");
}
}
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;
}
PassRefPtr<IdentifierNode> parseIdentifierName()
{
MetaNode node = this->createNode();
ALLOC_TOKEN(token);
this->nextToken(token);
if (!this->isIdentifierName(token)) {
this->throwUnexpectedToken(token);
}
return this->finalize(node, finishIdentifier(token, true));
}
ScanExpressionResult scanIdentifierName()
{
ALLOC_TOKEN(token);
this->nextToken(token);
if (!this->isIdentifierName(token)) {
this->throwUnexpectedToken(token);
}
return finishScanIdentifier(token, false);
}
template <typename T, bool isParse>
T newExpression()
{
this->nextToken();
if (this->match(Period)) {
this->nextToken();
if (this->lookahead.type == Token::IdentifierToken && this->context->inFunctionBody && this->lookahead.relatedSource() == "target") {
// TODO
RefPtr<IdentifierNode> property = this->parseIdentifierName();
this->throwError("Meta property is not supported yet");
RELEASE_ASSERT_NOT_REACHED();
// expr = new Node.MetaProperty(id, property);
} else {
this->throwUnexpectedToken(&this->lookahead);
}
}
if (isParse) {
RefPtr<Node> callee = this->isolateCoverGrammar(&Parser::leftHandSideExpression<Parse>);
ArgumentVector args;
if (this->match(LeftParenthesis)) {
args = this->parseArguments();
}
Node* expr = new NewExpressionNode(callee.get(), std::move(args));
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
MetaNode node = this->createNode();
return T(this->finalize(node, expr));
}
ScanExpressionResult callee = this->scanIsolateCoverGrammar(&Parser::leftHandSideExpression<Scan>);
if (this->match(LeftParenthesis)) {
this->scanArguments();
}
ScanExpressionResult expr(ASTNodeType::NewExpression);
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
return expr;
}
template <typename T, bool isParse>
T leftHandSideExpressionAllowCall()
{
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
bool previousAllowIn = this->context->allowIn;
this->context->allowIn = true;
RefPtr<Node> exprNode;
ScanExpressionResult expr;
if (this->context->inFunctionBody && this->matchKeyword(SuperKeyword)) {
this->nextToken();
if (!this->match(LeftParenthesis) && !this->match(Period) && !this->match(LeftSquareBracket)) {
this->throwUnexpectedToken(&this->lookahead);
}
if (this->context->inArrowFunction) {
scopeContexts.back()->m_hasArrowSuper = true;
}
if (isParse) {
insertUsingName(this->escargotContext->staticStrings().stringThis);
exprNode = this->finalize(this->createNode(), new SuperExpressionNode(this->lookahead.valuePunctuatorKind == LeftParenthesis));
} else {
expr = ScanExpressionResult(ASTNodeType::SuperExpression);
}
} else if (this->matchKeyword(NewKeyword)) {
if (isParse) {
exprNode = this->inheritCoverGrammar(&Parser::newExpression<Parse>);
} else {
expr = this->scanInheritCoverGrammar(&Parser::newExpression<Scan>);
}
} else {
if (isParse) {
exprNode = this->inheritCoverGrammar(&Parser::primaryExpression<Parse>);
} else {
expr = this->scanInheritCoverGrammar(&Parser::primaryExpression<Scan>);
}
}
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;
if (isParse) {
PassRefPtr<IdentifierNode> property = this->parseIdentifierName();
exprNode = this->finalize(this->startNode(startToken), new MemberExpressionNode(exprNode.get(), property.get(), true));
} else {
this->scanIdentifierName();
expr.setNodeType(ASTNodeType::MemberExpression);
}
this->trackUsingNames = trackUsingNamesBefore;
} else if (this->lookahead.valuePunctuatorKind == LeftParenthesis) {
this->context->isBindingElement = false;
this->context->isAssignmentTarget = false;
if (isParse) {
exprNode = this->finalize(this->startNode(startToken), new CallExpressionNode(exprNode.get(), this->parseArguments()));
} else {
testCalleeExpressionInScan(expr);
this->scanArguments();
expr.setNodeType(ASTNodeType::CallExpression);
}
} else if (this->lookahead.valuePunctuatorKind == LeftSquareBracket) {
this->context->isBindingElement = false;
this->context->isAssignmentTarget = true;
this->nextToken();
if (isParse) {
RefPtr<Node> property = this->isolateCoverGrammar(&Parser::expression<Parse>);
exprNode = this->finalize(this->startNode(startToken), new MemberExpressionNode(exprNode.get(), property.get(), false));
} else {
this->scanIsolateCoverGrammar(&Parser::expression<Scan>);
expr.setNodeType(ASTNodeType::MemberExpression);
}
this->expect(RightSquareBracket);
} else {
break;
}
} else if (this->lookahead.type == Token::TemplateToken && this->lookahead.valueTemplate->head) {
RefPtr<Node> quasi = this->parseTemplateLiteral();
// Note: exprNode is nullptr for Scan
exprNode = this->convertTaggedTempleateExpressionToCallExpression(this->startNode(startToken), this->finalize(this->startNode(startToken), new TaggedTemplateExpressionNode(exprNode.get(), quasi.get())));
if (!isParse) {
expr.setNodeType(exprNode->type());
}
} else {
break;
}
}
this->context->allowIn = previousAllowIn;
if (isParse) {
return exprNode.release();
}
return expr;
}
void testCalleeExpressionInScan(ScanExpressionResult callee)
{
if (callee == ASTNodeType::Identifier && lastScanIdentifierName == escargotContext->staticStrings().eval) {
scopeContexts.back()->m_hasEval = true;
if (this->context->inArrowFunction) {
insertUsingName(this->escargotContext->staticStrings().stringThis);
}
}
}
template <typename T, bool isParse>
T super()
{
MetaNode node = this->createNode();
this->expectKeyword(SuperKeyword);
if (!this->match(LeftSquareBracket) && !this->match(Period)) {
this->throwUnexpectedToken(&this->lookahead);
}
if (this->context->inArrowFunction) {
scopeContexts.back()->m_hasArrowSuper = true;
}
if (isParse) {
return T(this->finalize(node, new SuperExpressionNode(false)));
}
return ScanExpressionResult(ASTNodeType::SuperExpression);
}
template <typename T, bool isParse>
T leftHandSideExpression()
{
// assert(this->context->allowIn, 'callee of new expression always allow in keyword.');
ASSERT(this->context->allowIn);
RefPtr<Node> exprNode;
ScanExpressionResult expr;
if (this->matchKeyword(SuperKeyword) && this->context->inFunctionBody) {
if (isParse) {
exprNode = this->super<Parse>();
} else {
this->super<Scan>();
}
} else if (this->matchKeyword(NewKeyword)) {
if (isParse) {
exprNode = this->inheritCoverGrammar(&Parser::newExpression<Parse>);
} else {
this->scanInheritCoverGrammar(&Parser::newExpression<Scan>);
}
} else {
if (isParse) {
exprNode = this->inheritCoverGrammar(&Parser::primaryExpression<Parse>);
} else {
this->scanInheritCoverGrammar(&Parser::primaryExpression<Scan>);
}
}
MetaNode node = this->startNode(&this->lookahead);
while (true) {
if (this->match(LeftSquareBracket)) {
this->context->isBindingElement = false;
this->context->isAssignmentTarget = true;
this->expect(LeftSquareBracket);
if (isParse) {
RefPtr<Node> property = this->isolateCoverGrammar(&Parser::expression<Parse>);
exprNode = this->finalize(node, new MemberExpressionNode(exprNode.get(), property.get(), false));
} else {
this->scanIsolateCoverGrammar(&Parser::expression<Scan>);
expr.setNodeType(ASTNodeType::MemberExpression);
}
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;
if (isParse) {
RefPtr<IdentifierNode> property = this->parseIdentifierName();
exprNode = this->finalize(node, new MemberExpressionNode(exprNode.get(), property.get(), true));
} else {
this->scanIdentifierName();
expr.setNodeType(ASTNodeType::MemberExpression);
}
this->trackUsingNames = trackUsingNamesBefore;
} else if (this->lookahead.type == Token::TemplateToken && this->lookahead.valueTemplate->head) {
RefPtr<Node> quasi = this->parseTemplateLiteral();
// Note: exprNode is nullptr for Scan
exprNode = this->convertTaggedTempleateExpressionToCallExpression(node, this->finalize(node, new TaggedTemplateExpressionNode(exprNode.get(), quasi.get())).get());
if (!isParse) {
expr.setNodeType(exprNode->type());
}
} else {
break;
}
}
if (isParse) {
return exprNode.release();
}
return expr;
}
PassRefPtr<Node> convertTaggedTempleateExpressionToCallExpression(MetaNode node, RefPtr<TaggedTemplateExpressionNode> taggedTemplateExpression)
{
TemplateLiteralNode* templateLiteral = (TemplateLiteralNode*)taggedTemplateExpression->quasi();
ArgumentVector args;
ExpressionNodeVector elements;
for (size_t i = 0; i < templateLiteral->quasis()->size(); i++) {
UTF16StringData& sd = (*templateLiteral->quasis())[i]->value;
String* str = new UTF16String(std::move(sd));
elements.push_back(this->finalize(node, new LiteralNode(Value(str))));
}
RefPtr<ArrayExpressionNode> arrayExpressionForRaw;
{
ExpressionNodeVector elements;
for (size_t i = 0; i < templateLiteral->quasis()->size(); i++) {
String* str = (*templateLiteral->quasis())[i]->raw;
elements.push_back(this->finalize(node, new LiteralNode(Value(str))));
}
arrayExpressionForRaw = this->finalize(node, new ArrayExpressionNode(std::move(elements)));
}
RefPtr<ArrayExpressionNode> quasiVector = this->finalize(node, new ArrayExpressionNode(std::move(elements), this->escargotContext->staticStrings().raw, arrayExpressionForRaw.get()));
args.push_back(quasiVector.get());
for (size_t i = 0; i < templateLiteral->expressions().size(); i++) {
args.push_back(templateLiteral->expressions()[i]);
}
return this->finalize(node, new CallExpressionNode(taggedTemplateExpression->expr(), std::move(args)));
}
// ECMA-262 12.4 Update Expressions
template <typename T, bool isParse>
T updateExpression()
{
RefPtr<Node> exprNode;
ScanExpressionResult expr;
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
if (this->match(PlusPlus) || this->match(MinusMinus)) {
bool isPlus = this->match(PlusPlus);
ALLOC_TOKEN(token);
this->nextToken(token);
if (isParse) {
exprNode = this->inheritCoverGrammar(&Parser::unaryExpression<Parse>);
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(((IdentifierNode*)exprNode.get())->name())) {
this->throwError(Messages::StrictLHSPrefix);
}
} else {
expr = this->scanInheritCoverGrammar(&Parser::unaryExpression<Scan>);
if (expr == ASTNodeType::Literal || expr == ASTNodeType::ThisExpression) {
this->throwError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
}
if (this->context->strict && expr == ASTNodeType::Identifier && this->scanner->isRestrictedWord(lastScanIdentifierName)) {
this->throwError(Messages::StrictLHSPrefix);
}
}
if (!this->context->isAssignmentTarget && this->context->strict) {
this->throwError(Messages::InvalidLHSInAssignment);
}
if (isParse) {
MetaNode node = this->startNode(startToken);
if (isPlus) {
exprNode = this->finalize(node, new UpdateExpressionIncrementPrefixNode(exprNode.get()));
} else {
exprNode = this->finalize(node, new UpdateExpressionDecrementPrefixNode(exprNode.get()));
}
} else {
if (isPlus) {
expr = ScanExpressionResult(ASTNodeType::UpdateExpressionIncrementPrefix);
} else {
expr = ScanExpressionResult(ASTNodeType::UpdateExpressionDecrementPrefix);
}
}
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
} else {
if (isParse) {
exprNode = this->inheritCoverGrammar(&Parser::leftHandSideExpressionAllowCall<Parse>);
} else {
expr = this->scanInheritCoverGrammar(&Parser::leftHandSideExpressionAllowCall<Scan>);
}
if (!this->hasLineTerminator && this->lookahead.type == Token::PunctuatorToken && (this->match(PlusPlus) || this->match(MinusMinus))) {
bool isPlus = this->match(PlusPlus);
if (isParse && this->context->strict && exprNode->isIdentifier() && this->scanner->isRestrictedWord(((IdentifierNode*)exprNode.get())->name())) {
this->throwError(Messages::StrictLHSPostfix);
}
if (!isParse && this->context->strict && expr == ASTNodeType::Identifier && this->scanner->isRestrictedWord(lastScanIdentifierName)) {
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 (isParse) {
if (exprNode->isLiteral() || exprNode->type() == ASTNodeType::ThisExpression) {
this->throwError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
}
if (isPlus) {
exprNode = this->finalize(this->startNode(startToken), new UpdateExpressionIncrementPostfixNode(exprNode.get()));
} else {
exprNode = this->finalize(this->startNode(startToken), new UpdateExpressionDecrementPostfixNode(exprNode.get()));
}
} else {
if (expr == ASTNodeType::Literal || expr == ASTNodeType::ThisExpression) {
this->throwError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
}
if (isPlus) {
expr = ScanExpressionResult(ASTNodeType::UpdateExpressionIncrementPostfix);
} else {
expr = ScanExpressionResult(ASTNodeType::UpdateExpressionDecrementPostfix);
}
}
}
}
if (isParse) {
return exprNode.release();
}
return expr;
}
// ECMA-262 12.5 Unary Operators
template <typename T, bool isParse>
T unaryExpression()
{
if (this->lookahead.type == Token::PunctuatorToken) {
auto punctuatorsKind = this->lookahead.valuePunctuatorKind;
RefPtr<Node> exprNode;
if (punctuatorsKind == Plus) {
this->nextToken();
if (isParse) {
MetaNode node = this->startNode(&this->lookahead);
RefPtr<Node> subExpr = this->inheritCoverGrammar(&Parser::unaryExpression<Parse>);
exprNode = this->finalize(node, new UnaryExpressionPlusNode(subExpr.get()));
} else {
this->scanInheritCoverGrammar(&Parser::unaryExpression<Scan>);
}
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
if (isParse) {
return exprNode.release();
}
return ScanExpressionResult(ASTNodeType::UnaryExpressionPlus);
} else if (punctuatorsKind == Minus) {
this->nextToken();
if (isParse) {
MetaNode node = this->startNode(&this->lookahead);
RefPtr<Node> subExpr = this->inheritCoverGrammar(&Parser::unaryExpression<Parse>);
exprNode = this->finalize(node, new UnaryExpressionMinusNode(subExpr.get()));
} else {
this->scanInheritCoverGrammar(&Parser::unaryExpression<Scan>);
}
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
if (isParse) {
return exprNode.release();
}
return ScanExpressionResult(ASTNodeType::UnaryExpressionMinus);
} else if (punctuatorsKind == Wave) {
this->nextToken();
if (isParse) {
MetaNode node = this->startNode(&this->lookahead);
RefPtr<Node> subExpr = this->inheritCoverGrammar(&Parser::unaryExpression<Parse>);
exprNode = this->finalize(node, new UnaryExpressionBitwiseNotNode(subExpr.get()));
} else {
this->scanInheritCoverGrammar(&Parser::unaryExpression<Scan>);
}
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
if (isParse) {
return exprNode.release();
}
return ScanExpressionResult(ASTNodeType::UnaryExpressionBitwiseNot);
} else if (punctuatorsKind == ExclamationMark) {
this->nextToken();
if (isParse) {
MetaNode node = this->startNode(&this->lookahead);
RefPtr<Node> subExpr = this->inheritCoverGrammar(&Parser::unaryExpression<Parse>);
exprNode = this->finalize(node, new UnaryExpressionLogicalNotNode(subExpr.get()));
} else {
this->scanInheritCoverGrammar(&Parser::unaryExpression<Scan>);
}
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
if (isParse) {
return exprNode.release();
}
return ScanExpressionResult(ASTNodeType::UnaryExpressionBitwiseNot);
}
}
bool isKeyword = this->lookahead.type == Token::KeywordToken;
if (isKeyword) {
RefPtr<Node> exprNode;
if (this->lookahead.valueKeywordKind == DeleteKeyword) {
this->nextToken();
if (isParse) {
MetaNode node = this->startNode(&this->lookahead);
RefPtr<Node> subExpr = this->inheritCoverGrammar(&Parser::unaryExpression<Parse>);
if (this->context->strict && subExpr->isIdentifier()) {
this->throwError(Messages::StrictDelete);
}
if (subExpr->isIdentifier()) {
this->scopeContexts.back()->m_hasEvaluateBindingId = true;
}
exprNode = this->finalize(node, new UnaryExpressionDeleteNode(subExpr.get()));
} else {
ScanExpressionResult subExpr = this->scanInheritCoverGrammar(&Parser::unaryExpression<Scan>);
if (this->context->strict && subExpr == ASTNodeType::Identifier) {
this->throwError(Messages::StrictDelete);
}
if (subExpr == ASTNodeType::Identifier) {
this->scopeContexts.back()->m_hasEvaluateBindingId = true;
}
}
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
if (isParse) {
return exprNode.release();
}
return ScanExpressionResult(ASTNodeType::UnaryExpressionDelete);
} else if (this->lookahead.valueKeywordKind == VoidKeyword) {
this->nextToken();
if (isParse) {
MetaNode node = this->startNode(&this->lookahead);
RefPtr<Node> subExpr = this->inheritCoverGrammar(&Parser::unaryExpression<Parse>);
exprNode = this->finalize(node, new UnaryExpressionVoidNode(subExpr.get()));
} else {
this->scanInheritCoverGrammar(&Parser::unaryExpression<Scan>);
}
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
if (isParse) {
return exprNode.release();
}
return ScanExpressionResult(ASTNodeType::UnaryExpressionVoid);
} else if (this->lookahead.valueKeywordKind == TypeofKeyword) {
this->nextToken();
if (isParse) {
MetaNode node = this->startNode(&this->lookahead);
RefPtr<Node> subExpr = this->inheritCoverGrammar(&Parser::unaryExpression<Parse>);
if (subExpr->isIdentifier()) {
AtomicString s = subExpr->asIdentifier()->name();
if (!this->scopeContexts.back()->hasName(s)) {
this->scopeContexts.back()->m_hasEvaluateBindingId = true;
}
}
exprNode = this->finalize(node, new UnaryExpressionTypeOfNode(subExpr.get()));
} else {
ScanExpressionResult subExpr = this->scanInheritCoverGrammar(&Parser::unaryExpression<Scan>);
if (subExpr == ASTNodeType::Identifier) {
if (!this->scopeContexts.back()->hasName(lastScanIdentifierName)) {
this->scopeContexts.back()->m_hasEvaluateBindingId = true;
}
}
}
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
if (isParse) {
return exprNode.release();
}
return ScanExpressionResult(ASTNodeType::UnaryExpressionTypeOf);
}
}
return this->updateExpression<T, isParse>();
}
PassRefPtr<Node> parseExponentiationExpression()
{
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
RefPtr<Node> expr = this->inheritCoverGrammar(&Parser::unaryExpression<Parse>);
// 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;
}
ScanExpressionResult scanExponentiationExpression()
{
ScanExpressionResult expr = this->scanInheritCoverGrammar(&Parser::unaryExpression<Scan>);
// 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;
}
PassRefPtr<Node> parseBinaryExpression()
{
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
RefPtr<Node> expr = this->inheritCoverGrammar(&Parser::parseExponentiationExpression);
ALLOC_TOKEN(token);
*token = this->lookahead;
//std::vector<RefPtr<Scanner::ScannerResult>, GCUtil::gc_malloc_ignore_off_page_allocator<RefPtr<Scanner::ScannerResult>>> tokenKeeper;
int prec = this->binaryPrecedence(token);
if (prec > 0) {
this->nextToken();
token->prec = prec;
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
Vector<Scanner::ScannerResult, GCUtil::gc_malloc_ignore_off_page_allocator<Scanner::ScannerResult>> markers;
markers.push_back(*startToken);
markers.push_back(this->lookahead);
RefPtr<Node> left = expr;
RefPtr<Node> right = this->isolateCoverGrammar(&Parser::parseExponentiationExpression);
//std::vector<void*, GCUtil::gc_malloc_ignore_off_page_allocator<void*>> stack;
NodeVector stack;
Vector<Scanner::ScannerResult, GCUtil::gc_malloc_ignore_off_page_allocator<Scanner::ScannerResult>> tokenStack;
stack.push_back(left);
stack.push_back(right);
tokenStack.push_back(*token);
while (true) {
prec = this->binaryPrecedence(&this->lookahead);
if (prec <= 0) {
break;
}
// Reduce: make a binary expression from the three topmost entries.
while ((stack.size() > 1) && (prec <= tokenStack.back().prec)) {
right = stack.back();
stack.pop_back();
Scanner::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(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(&Parser::parseExponentiationExpression);
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(stack[i - 1].get(), expr.get(), &tokenStack.back()));
markers.pop_back();
tokenStack.pop_back();
i--;
}
RELEASE_ASSERT(i == 0);
}
return expr.release();
}
ScanExpressionResult scanBinaryExpressions()
{
ScanExpressionResult expr = this->scanInheritCoverGrammar(&Parser::scanExponentiationExpression);
ALLOC_TOKEN(token);
*token = this->lookahead;
//std::vector<RefPtr<Scanner::ScannerResult>, GCUtil::gc_malloc_ignore_off_page_allocator<RefPtr<Scanner::ScannerResult>>> tokenKeeper;
int prec = this->binaryPrecedence(token);
if (prec > 0) {
this->nextToken();
token->prec = prec;
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
ScanExpressionResult left = expr;
ScanExpressionResult right = this->scanIsolateCoverGrammar(&Parser::scanExponentiationExpression);
std::vector<void*, GCUtil::gc_malloc_ignore_off_page_allocator<void*>> stack;
stack.push_back(new ScanExpressionResult(left));
stack.push_back(token);
stack.push_back(new ScanExpressionResult(right));
while (true) {
prec = this->binaryPrecedence(&this->lookahead);
if (prec <= 0) {
break;
}
// Reduce: make a binary expression from the three topmost entries.
while ((stack.size() > 2) && (prec <= ((Scanner::ScannerResult*)stack[stack.size() - 2])->prec)) {
right = *((ScanExpressionResult*)stack.back());
delete (ScanExpressionResult*)stack.back();
stack.pop_back();
Scanner::ScannerResult* operator_ = (Scanner::ScannerResult*)stack.back();
stack.pop_back();
left = *((ScanExpressionResult*)stack.back());
delete (ScanExpressionResult*)stack.back();
stack.pop_back();
auto e = scanBinaryExpression(left, right, operator_);
stack.push_back(new ScanExpressionResult(e));
}
// Shift.
this->nextToken(token);
token->prec = prec;
stack.push_back(token);
auto e = this->scanIsolateCoverGrammar(&Parser::scanExponentiationExpression);
stack.push_back(new ScanExpressionResult(e));
}
// Final reduce to clean-up the stack.
size_t i = stack.size() - 1;
expr = *((ScanExpressionResult*)stack[i]);
delete (ScanExpressionResult*)stack[i];
while (i > 1) {
expr = scanBinaryExpression(*((ScanExpressionResult*)stack[i - 2]), expr, (Scanner::ScannerResult*)stack[i - 1]);
delete (ScanExpressionResult*)stack[i - 2];
i -= 2;
}
RELEASE_ASSERT(i == 0);
}
return expr;
}
Node* finishBinaryExpression(Node* left, Node* right, Scanner::ScannerResult* token)
{
if (token->type == Token::PunctuatorToken) {
PunctuatorKind oper = token->valuePunctuatorKind;
// Additive Operators
switch (oper) {
case Plus:
return new BinaryExpressionPlusNode(left, right);
case Minus:
return new BinaryExpressionMinusNode(left, right);
case LeftShift:
return new BinaryExpressionLeftShiftNode(left, right);
case RightShift:
return new BinaryExpressionSignedRightShiftNode(left, right);
case UnsignedRightShift:
return new BinaryExpressionUnsignedRightShiftNode(left, right);
case Multiply:
return new BinaryExpressionMultiplyNode(left, right);
case Divide:
return new BinaryExpressionDivisionNode(left, right);
case Mod:
return new BinaryExpressionModNode(left, right);
case LeftInequality:
return new BinaryExpressionLessThanNode(left, right);
case RightInequality:
return new BinaryExpressionGreaterThanNode(left, right);
case LeftInequalityEqual:
return new BinaryExpressionLessThanOrEqualNode(left, right);
case RightInequalityEqual:
return new BinaryExpressionGreaterThanOrEqualNode(left, right);
case Equal:
return new BinaryExpressionEqualNode(left, right);
case NotEqual:
return new BinaryExpressionNotEqualNode(left, right);
case StrictEqual:
return new BinaryExpressionStrictEqualNode(left, right);
case NotStrictEqual:
return new BinaryExpressionNotStrictEqualNode(left, right);
case BitwiseAnd:
return new BinaryExpressionBitwiseAndNode(left, right);
case BitwiseXor:
return new BinaryExpressionBitwiseXorNode(left, right);
case BitwiseOr:
return new BinaryExpressionBitwiseOrNode(left, right);
case LogicalOr:
return new BinaryExpressionLogicalOrNode(left, right);
case LogicalAnd:
return new BinaryExpressionLogicalAndNode(left, right);
default:
RELEASE_ASSERT_NOT_REACHED();
}
} else {
ASSERT(token->type == Token::KeywordToken);
switch (token->valueKeywordKind) {
case InKeyword:
return new BinaryExpressionInNode(left, right);
case KeywordKind::InstanceofKeyword:
return new BinaryExpressionInstanceOfNode(left, right);
default:
RELEASE_ASSERT_NOT_REACHED();
}
}
}
ScanExpressionResult scanBinaryExpression(ScanExpressionResult left, ScanExpressionResult right, Scanner::ScannerResult* token)
{
ScanExpressionResult nd(ASTNodeType::ASTNodeTypeError);
if (token->type == Token::PunctuatorToken) {
PunctuatorKind oper = token->valuePunctuatorKind;
// Additive Operators
switch (oper) {
case Plus:
nd.setNodeType(ASTNodeType::BinaryExpressionPlus);
return nd;
case Minus:
nd.setNodeType(ASTNodeType::BinaryExpressionMinus);
return nd;
case LeftShift: //Bitse Shift Oerators
nd.setNodeType(ASTNodeType::BinaryExpressionLeftShift);
return nd;
case RightShift:
nd.setNodeType(ASTNodeType::BinaryExpressionSignedRightShift);
return nd;
case UnsignedRightShift:
nd.setNodeType(ASTNodeType::BinaryExpressionUnsignedRightShift);
return nd;
case Multiply: // Multiplicative Operators
nd.setNodeType(ASTNodeType::BinaryExpressionMultiply);
return nd;
case Divide:
nd.setNodeType(ASTNodeType::BinaryExpressionDivison);
return nd;
case Mod:
nd.setNodeType(ASTNodeType::BinaryExpressionMod);
return nd;
case LeftInequality: //Relative Operators
nd.setNodeType(ASTNodeType::BinaryExpressionLessThan);
return nd;
case RightInequality:
nd.setNodeType(ASTNodeType::BinaryExpressionGreaterThan);
return nd;
case LeftInequalityEqual:
nd.setNodeType(ASTNodeType::BinaryExpressionLessThanOrEqual);
return nd;
case RightInequalityEqual:
nd.setNodeType(ASTNodeType::BinaryExpressionGreaterThanOrEqual);
return nd;
case Equal: //Equality Operators
nd.setNodeType(ASTNodeType::BinaryExpressionEqual);
return nd;
case NotEqual:
nd.setNodeType(ASTNodeType::BinaryExpressionNotEqual);
return nd;
case StrictEqual:
nd.setNodeType(ASTNodeType::BinaryExpressionStrictEqual);
return nd;
case NotStrictEqual:
nd.setNodeType(ASTNodeType::BinaryExpressionNotStrictEqual);
return nd;
case BitwiseAnd: //Binary Bitwise Operator
nd.setNodeType(ASTNodeType::BinaryExpressionBitwiseAnd);
return nd;
case BitwiseXor:
nd.setNodeType(ASTNodeType::BinaryExpressionBitwiseXor);
return nd;
case BitwiseOr:
nd.setNodeType(ASTNodeType::BinaryExpressionBitwiseOr);
return nd;
case LogicalOr:
nd.setNodeType(ASTNodeType::BinaryExpressionLogicalOr);
return nd;
case LogicalAnd:
nd.setNodeType(ASTNodeType::BinaryExpressionLogicalAnd);
return nd;
default:
RELEASE_ASSERT_NOT_REACHED();
}
} else {
ASSERT(token->type == Token::KeywordToken);
switch (token->valueKeywordKind) {
case InKeyword:
nd.setNodeType(ASTNodeType::BinaryExpressionIn);
return nd;
case KeywordKind::InstanceofKeyword:
nd.setNodeType(ASTNodeType::BinaryExpressionInstanceOf);
return nd;
default:
RELEASE_ASSERT_NOT_REACHED();
}
}
}
// ECMA-262 12.14 Conditional Operator
template <typename T, bool isParse>
T conditionalExpression()
{
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
RefPtr<Node> exprNode;
ScanExpressionResult expr;
if (isParse) {
exprNode = this->inheritCoverGrammar(&Parser::parseBinaryExpression);
} else {
expr = this->scanInheritCoverGrammar(&Parser::scanBinaryExpressions);
}
if (this->match(GuessMark)) {
RefPtr<Node> consequent;
this->nextToken();
bool previousAllowIn = this->context->allowIn;
this->context->allowIn = true;
if (isParse) {
consequent = this->isolateCoverGrammar(&Parser::assignmentExpression<Parse>);
} else {
this->scanIsolateCoverGrammar(&Parser::assignmentExpression<Scan>);
}
this->context->allowIn = previousAllowIn;
this->expect(Colon);
if (isParse) {
RefPtr<Node> alternate = this->isolateCoverGrammar(&Parser::assignmentExpression<Parse>);
exprNode = this->finalize(this->startNode(startToken), new ConditionalExpressionNode(exprNode.get(), consequent.get(), alternate.get()));
} else {
this->scanIsolateCoverGrammar(&Parser::assignmentExpression<Scan>);
}
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
}
if (isParse) {
return exprNode.release();
}
return expr;
}
// ECMA-262 12.15 Assignment Operators
template <typename T, bool isParse>
T assignmentExpression()
{
RefPtr<Node> exprNode;
ScanExpressionResult expr;
if (!this->context->allowYield && this->matchKeyword(YieldKeyword)) {
if (isParse) {
exprNode = this->yieldExpression<ParseAs(YieldExpressionNode)>();
} else {
expr = this->yieldExpression<Scan>();
}
} else {
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
ALLOC_TOKEN(token);
*token = *startToken;
ASTNodeType type;
MetaNode startNode = this->createNode();
Marker startMarker = this->lastMarker;
if (isParse) {
exprNode = this->conditionalExpression<Parse>();
type = exprNode->type();
} else {
expr = this->conditionalExpression<Scan>();
type = expr;
}
/*
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;
if (!isParse) {
// rewind scanner for return to normal mode
this->scanner->index = startMarker.index;
this->scanner->lineNumber = startMarker.lineNumber;
this->scanner->lineStart = startMarker.lineStart;
this->nextToken();
exprNode = this->conditionalExpression<Parse>();
}
ParseFormalParametersResult list;
pushScopeContext(AtomicString());
if (type == Identifier) {
list = ParseFormalParametersResult();
list.params.push_back(exprNode);
list.valid = true;
} else {
this->scanner->index = startMarker.index;
this->scanner->lineNumber = startMarker.lineNumber;
this->scanner->lineStart = startMarker.lineStart;
this->nextToken();
this->expect(LeftParenthesis);
list = this->parseFormalParameters();
scopeContexts.back()->m_paramsStart.index = startMarker.index;
}
if (isParse) {
exprNode.release();
}
if (list.valid) {
if (this->hasLineTerminator) {
this->throwUnexpectedToken(&this->lookahead);
}
this->context->firstCoverInitializedNameError.reset();
extractNamesFromFunctionParams(list.params);
bool previousStrict = this->context->strict;
bool previousAllowYield = this->context->allowYield;
bool previousInArrowFunction = this->context->inArrowFunction;
this->context->allowYield = true;
this->context->inArrowFunction = true;
MetaNode node = this->startNode(startToken);
MetaNode nodeStart = this->createNode();
this->expect(Arrow);
RefPtr<Node> body = this->match(LeftBrace) ? this->parseFunctionSourceElements() : this->isolateCoverGrammar(&Parser::assignmentExpression<Parse>);
bool isExpression = body->type() != BlockStatement;
if (isExpression) {
if (this->config.parseSingleFunction) {
ASSERT(this->config.parseSingleFunctionChildIndex > 0);
this->config.parseSingleFunctionChildIndex++;
}
scopeContexts.back()->m_locStart.line = nodeStart.line;
scopeContexts.back()->m_locStart.column = nodeStart.column;
scopeContexts.back()->m_locStart.index = nodeStart.index;
scopeContexts.back()->m_locEnd.index = this->lastMarker.index;
#ifndef NDEBUG
scopeContexts.back()->m_locEnd.line = this->lastMarker.lineNumber;
scopeContexts.back()->m_locEnd.column = this->lastMarker.index - this->lastMarker.lineStart;
#endif
}
if (this->context->strict && list.firstRestricted) {
this->throwUnexpectedToken(&list.firstRestricted, list.message);
}
if (this->context->strict && list.stricted) {
this->throwUnexpectedToken(&list.stricted, list.message);
}
exprNode = this->finalize(node, new ArrowFunctionExpressionNode(std::move(list.params), body.get(), popScopeContext(node), isExpression)); //TODO
if (!isParse) {
expr.setNodeType(ASTNodeType::ArrowFunctionExpression);
}
this->context->strict = previousStrict;
this->context->allowYield = previousAllowYield;
this->context->inArrowFunction = previousInArrowFunction;
}
} else {
if (this->matchAssign()) {
if (!this->context->isAssignmentTarget && this->context->strict) {
this->throwError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
}
if (this->context->strict && type == Identifier) {
AtomicString name;
if (isParse) {
IdentifierNode* id = exprNode->asIdentifier();
name = id->name();
} else {
name = lastScanIdentifierName;
}
if (this->scanner->isRestrictedWord(name)) {
this->throwUnexpectedToken(token, Messages::StrictLHSAssignment);
}
if (this->scanner->isStrictModeReservedWord(name)) {
this->throwUnexpectedToken(token, Messages::StrictReservedWord);
}
}
if (!this->match(Substitution)) {
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
} else if (isParse) {
this->reinterpretExpressionAsPattern(exprNode.get());
if (exprNode->isLiteral() || exprNode->type() == ASTNodeType::ThisExpression) {
this->throwError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
}
} else {
this->scanReinterpretExpressionAsPattern(expr);
if (expr == ASTNodeType::Literal || expr == ASTNodeType::ThisExpression) {
this->throwError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
}
}
this->nextToken(token);
RefPtr<Node> rightNode;
Node* exprResult;
if (isParse) {
rightNode = this->isolateCoverGrammar(&Parser::assignmentExpression<Parse>);
} else {
this->scanIsolateCoverGrammar(&Parser::assignmentExpression<Scan>);
}
switch (token->valuePunctuatorKind) {
case Substitution:
if (isParse) {
exprResult = new AssignmentExpressionSimpleNode(exprNode.get(), rightNode.get());
break;
}
expr.setNodeType(ASTNodeType::AssignmentExpressionSimple);
break;
case PlusEqual:
if (isParse) {
exprResult = new AssignmentExpressionPlusNode(exprNode.get(), rightNode.get());
break;
}
expr.setNodeType(ASTNodeType::AssignmentExpressionPlus);
break;
case MinusEqual:
if (isParse) {
exprResult = new AssignmentExpressionMinusNode(exprNode.get(), rightNode.get());
break;
}
expr.setNodeType(ASTNodeType::AssignmentExpressionMinus);
break;
case MultiplyEqual:
if (isParse) {
exprResult = new AssignmentExpressionMultiplyNode(exprNode.get(), rightNode.get());
break;
}
expr.setNodeType(ASTNodeType::AssignmentExpressionMultiply);
break;
case DivideEqual:
if (isParse) {
exprResult = new AssignmentExpressionDivisionNode(exprNode.get(), rightNode.get());
break;
}
expr.setNodeType(ASTNodeType::AssignmentExpressionDivision);
break;
case ModEqual:
if (isParse) {
exprResult = new AssignmentExpressionModNode(exprNode.get(), rightNode.get());
break;
}
expr.setNodeType(ASTNodeType::AssignmentExpressionMod);
break;
case LeftShiftEqual:
if (isParse) {
exprResult = new AssignmentExpressionLeftShiftNode(exprNode.get(), rightNode.get());
break;
}
expr.setNodeType(ASTNodeType::AssignmentExpressionLeftShift);
break;
case RightShiftEqual:
if (isParse) {
exprResult = new AssignmentExpressionSignedRightShiftNode(exprNode.get(), rightNode.get());
break;
}
expr.setNodeType(ASTNodeType::AssignmentExpressionSignedRightShift);
break;
case UnsignedRightShiftEqual:
if (isParse) {
exprResult = new AssignmentExpressionUnsignedShiftNode(exprNode.get(), rightNode.get());
break;
}
expr.setNodeType(ASTNodeType::AssignmentExpressionUnsignedRightShift);
break;
case BitwiseXorEqual:
if (isParse) {
exprResult = new AssignmentExpressionBitwiseXorNode(exprNode.get(), rightNode.get());
break;
}
expr.setNodeType(ASTNodeType::AssignmentExpressionBitwiseXor);
break;
case BitwiseAndEqual:
if (isParse) {
exprResult = new AssignmentExpressionBitwiseAndNode(exprNode.get(), rightNode.get());
break;
}
expr.setNodeType(ASTNodeType::AssignmentExpressionBitwiseAnd);
break;
case BitwiseOrEqual:
if (isParse) {
exprResult = new AssignmentExpressionBitwiseOrNode(exprNode.get(), rightNode.get());
break;
}
expr.setNodeType(ASTNodeType::AssignmentExpressionBitwiseOr);
break;
default:
RELEASE_ASSERT_NOT_REACHED();
}
if (isParse) {
exprNode = this->finalize(this->startNode(startToken), exprResult);
}
this->context->firstCoverInitializedNameError.reset();
}
}
}
if (isParse) {
return exprNode.release();
}
return expr;
}
// ECMA-262 12.16 Comma Operator
template <typename T, bool isParse>
T expression()
{
ALLOC_TOKEN(startToken);
*startToken = this->lookahead;
RefPtr<Node> exprNode;
ScanExpressionResult expr;
if (isParse) {
exprNode = this->isolateCoverGrammar(&Parser::assignmentExpression<Parse>);
} else {
expr = this->scanIsolateCoverGrammar(&Parser::assignmentExpression<Scan>);
}
if (this->match(Comma)) {
ExpressionNodeVector expressions;
if (isParse) {
expressions.push_back(exprNode);
}
while (this->startMarker.index < this->scanner->length) {
if (!this->match(Comma)) {
break;
}
this->nextToken();
if (isParse) {
expressions.push_back(this->isolateCoverGrammar(&Parser::assignmentExpression<Parse>));
} else {
this->scanIsolateCoverGrammar(&Parser::assignmentExpression<Scan>);
}
}
if (isParse) {
exprNode = this->finalize(this->startNode(startToken), new SequenceExpressionNode(std::move(expressions)));
} else {
expr = ScanExpressionResult(ASTNodeType::SequenceExpression);
}
}
if (isParse) {
return exprNode.release();
}
return expr;
}
// ECMA-262 13.2 Block
template <typename T, bool isParse>
T statementListItem()
{
RefPtr<StatementNode> statement;
this->context->isAssignmentTarget = true;
this->context->isBindingElement = true;
if (this->lookahead.type == KeywordToken) {
switch (this->lookahead.valueKeywordKind) {
case FunctionKeyword:
if (isParse) {
statement = this->parseFunctionDeclaration();
} else {
this->parseFunctionDeclaration();
}
break;
default:
if (isParse) {
statement = this->parseStatement();
} else {
this->scanStatement();
}
break;
}
} else if (isParse) {
statement = this->parseStatement();
} else {
this->scanStatement();
}
/*
if (this->lookahead.type === Token.Keyword) {
switch (this->lookahead.value) {
case 'export':
if (this->sourceType !== 'module') {
this->throwUnexpectedToken(this->lookahead, Messages.IllegalExportDeclaration);
}
statement = this->parseExportDeclaration();
break;
case 'import':
if (this->sourceType !== 'module') {
this->throwUnexpectedToken(this->lookahead, Messages.IllegalImportDeclaration);
}
statement = this->parseImportDeclaration();
break;
case 'const':
statement = this->parseLexicalDeclaration({ inFor: false });
break;
case 'function':
statement = this->parseFunctionDeclaration();
break;
case 'class':
statement = this->parseClassDeclaration();
break;
case 'let':
statement = this->isLexicalDeclaration() ? this->parseLexicalDeclaration({ inFor: false }) : this->parseStatement();
break;
default:
statement = this->parseStatement();
break;
}
} else {
statement = this->parseStatement();
}*/
if (isParse) {
return T(statement.release());
}
return T(nullptr);
}
template <typename T, bool isParse>
T block()
{
this->expect(LeftBrace);
RefPtr<StatementContainer> block;
StatementNode* referNode = nullptr;
if (isParse) {
block = StatementContainer::create();
}
while (true) {
if (this->match(RightBrace)) {
break;
}
if (isParse) {
referNode = block->appendChild(this->statementListItem<ParseAs(StatementNode)>().get(), referNode);
} else {
this->statementListItem<ScanAsVoid>();
}
}
this->expect(RightBrace);
if (isParse) {
MetaNode node = this->createNode();
return T(this->finalize(node, new BlockStatementNode(block.get())));
}
return T(nullptr);
}
/*
// ECMA-262 13.3.1 Let and Const Declarations
parseLexicalBinding(kind: string, options): Node.VariableDeclarator {
const node = this->createNode();
let params = [];
const id = this->parsePattern(params, kind);
// ECMA-262 12.2.1
if (this->context.strict && id.type === Syntax.Identifier) {
if (this->scanner.isRestrictedWord((<Node.Identifier>(id)).name)) {
this->throwError(Messages.StrictVarName);
}
}
let init = null;
if (kind === 'const') {
if (!this->matchKeyword('in') && !this->matchContextualKeyword('of')) {
this->expect('=');
init = this->isolateCoverGrammar(this->assignmentExpression<Parse>);
}
} else if ((!options.inFor && id.type !== Syntax.Identifier) || this->match('=')) {
this->expect('=');
init = this->isolateCoverGrammar(this->assignmentExpression<Parse>);
}
return this->finalize(node, new Node.VariableDeclarator(id, init));
}
parseBindingList(kind: string, options): Node.VariableDeclarator[] {
let list = [this->parseLexicalBinding(kind, options)];
while (this->match(',')) {
this->nextToken();
list.push(this->parseLexicalBinding(kind, options));
}
return list;
}
isLexicalDeclaration(): boolean {
const previousIndex = this->scanner.index;
const previousLineNumber = this->scanner.lineNumber;
const previousLineStart = this->scanner.lineStart;
this->collectComments();
const next = <any>this->scanner.lex();
this->scanner.index = previousIndex;
this->scanner.lineNumber = previousLineNumber;
this->scanner.lineStart = previousLineStart;
return (next.type === Token.Identifier) ||
(next.type === Token.Punctuator && next.value === '[') ||
(next.type === Token.Punctuator && next.value === '{') ||
(next.type === Token.Keyword && next.value === 'let') ||
(next.type === Token.Keyword && next.value === 'yield');
}
parseLexicalDeclaration(options): Node.VariableDeclaration {
const node = this->createNode();
const kind = this->nextToken().value;
assert(kind === 'let' || kind === 'const', 'Lexical declaration must be either let or const');
const declarations = this->parseBindingList(kind, options);
this->consumeSemicolon();
return this->finalize(node, new Node.VariableDeclaration(declarations, kind));
}
// ECMA-262 13.3.3 Destructuring Binding Patterns
parseBindingRestElement(params, kind: string): Node.RestElement {
const node = this->createNode();
this->expect('...');
params.push(this->lookahead);
const arg = this->parseVariableIdentifier(kind);
return this->finalize(node, new Node.RestElement(arg));
}
parseArrayPattern(params, kind: string): Node.ArrayPattern {
const node = this->createNode();
this->expect('[');
const elements: Node.ArrayPatternElement[] = [];
while (!this->match(']')) {
if (this->match(',')) {
this->nextToken();
elements.push(null);
} else {
if (this->match('...')) {
elements.push(this->parseBindingRestElement(params, kind));
break;
} else {
elements.push(this->parsePatternWithDefault(params, kind));
}
if (!this->match(']')) {
this->expect(',');
}
}
}
this->expect(']');
return this->finalize(node, new Node.ArrayPattern(elements));
}
parsePropertyPattern(params, kind: string): Node.Property {
const node = this->createNode();
let computed = false;
let shorthand = false;
const method = false;
let key: Node.PropertyKey;
let value: Node.PropertyValue;
if (this->lookahead.type === Token.Identifier) {
const keyToken = this->lookahead;
key = this->parseVariableIdentifier();
const init = this->finalize(node, new Node.Identifier(keyToken.value));
if (this->match('=')) {
params.push(keyToken);
shorthand = true;
this->nextToken();
const expr = this->assignmentExpression<Parse>();
value = this->finalize(this->startNode(keyToken), new Node.AssignmentPattern(init, expr));
} else if (!this->match(':')) {
params.push(keyToken);
shorthand = true;
value = init;
} else {
this->expect(':');
value = this->parsePatternWithDefault(params, kind);
}
} else {
computed = this->match('[');
key = this->parseObjectPropertyKey();
this->expect(':');
value = this->parsePatternWithDefault(params, kind);
}
return this->finalize(node, new Node.Property('init', key, computed, value, method, shorthand));
}
parseObjectPattern(params, kind: string): Node.ObjectPattern {
const node = this->createNode();
const properties: Node.Property[] = [];
this->expect('{');
while (!this->match('}')) {
properties.push(this->parsePropertyPattern(params, kind));
if (!this->match('}')) {
this->expect(',');
}
}
this->expect('}');
return this->finalize(node, new Node.ObjectPattern(properties));
}
parsePattern(params, kind?: string): Node.BindingIdentifier | Node.BindingPattern {
let pattern;
if (this->match('[')) {
pattern = this->parseArrayPattern(params, kind);
} else if (this->match('{')) {
pattern = this->parseObjectPattern(params, kind);
} else {
if (this->matchKeyword('let') && (kind === 'const' || kind === 'let')) {
this->throwUnexpectedToken(this->lookahead, Messages.UnexpectedToken);
}
params.push(this->lookahead);
pattern = this->parseVariableIdentifier(kind);
}
return pattern;
}
parsePatternWithDefault(params, kind?: string): Node.AssignmentPattern | Node.BindingIdentifier | Node.BindingPattern {
const startToken = this->lookahead;
let pattern = this->parsePattern(params, kind);
if (this->match('=')) {
this->nextToken();
const previousAllowYield = this->context.allowYield;
this->context.allowYield = true;
const right = this->isolateCoverGrammar(this->assignmentExpression<Parse>);
this->context.allowYield = previousAllowYield;
pattern = this->finalize(this->startNode(startToken), new Node.AssignmentPattern(pattern, right));
}
return pattern;
}
*/
// ECMA-262 13.3.2 Variable Statement
PassRefPtr<IdentifierNode> parseVariableIdentifier(KeywordKind kind = KeywordKindEnd)
{
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);
}
return this->finalize(node, finishIdentifier(token, true));
}
void scanVariableIdentifier(KeywordKind kind = KeywordKindEnd)
{
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);
}
finishScanIdentifier(token, true);
}
template <typename T, bool isParse>
T variableDeclaration(DeclarationOptions& options)
{
Vector<Scanner::ScannerResult, GCUtil::gc_malloc_ignore_off_page_allocator<Scanner::ScannerResult>> params;
RefPtr<Node> idNode;
ScanExpressionResult id;
bool isIdentifier;
AtomicString name;
if (isParse) {
idNode = this->pattern<Parse>(params, VarKeyword);
isIdentifier = (idNode->type() == Identifier);
if (isIdentifier) {
name = ((IdentifierNode*)idNode.get())->name();
}
} else {
id = this->pattern<Scan>(params, VarKeyword);
isIdentifier = (id == Identifier);
if (isIdentifier) {
name = lastScanIdentifierName;
}
}
// ECMA-262 12.2.1
if (this->context->strict && isIdentifier && this->scanner->isRestrictedWord(name)) {
this->throwError(Messages::StrictVarName);
}
if (isIdentifier && !this->config.parseSingleFunction) {
this->scopeContexts.back()->insertName(name, true);
}
RefPtr<Node> initNode;
if (this->match(Substitution)) {
this->nextToken();
if (isParse) {
initNode = this->isolateCoverGrammar(&Parser::assignmentExpression<Parse>);
} else {
this->scanIsolateCoverGrammar(&Parser::assignmentExpression<Scan>);
}
} else if (!isIdentifier && !options.inFor) {
this->expect(Substitution);
}
if (isParse) {
MetaNode node = this->createNode();
return T(this->finalize(node, new VariableDeclaratorNode(idNode.get(), initNode.get())));
}
return T(nullptr);
}
VariableDeclaratorVector parseVariableDeclarationList(DeclarationOptions& options)
{
DeclarationOptions opt;
opt.inFor = options.inFor;
VariableDeclaratorVector list;
list.push_back(this->variableDeclaration<ParseAs(VariableDeclaratorNode)>(opt));
while (this->match(Comma)) {
this->nextToken();
list.push_back(this->variableDeclaration<ParseAs(VariableDeclaratorNode)>(opt));
}
return list;
}
void scanVariableDeclarationList(DeclarationOptions& options)
{
DeclarationOptions opt;
opt.inFor = options.inFor;
size_t listSize = 0;
this->variableDeclaration<ScanAsVoid>(opt);
while (this->match(Comma)) {
this->nextToken();
this->variableDeclaration<ScanAsVoid>(opt);
}
}
PassRefPtr<VariableDeclarationNode> parseVariableStatement()
{
this->expectKeyword(VarKeyword);
MetaNode node = this->createNode();
DeclarationOptions opt;
opt.inFor = false;
VariableDeclaratorVector declarations = this->parseVariableDeclarationList(opt);
this->consumeSemicolon();
return this->finalize(node, new VariableDeclarationNode(std::move(declarations) /*, 'var'*/));
}
void scanVariableStatement()
{
this->expectKeyword(VarKeyword);
DeclarationOptions opt;
opt.inFor = false;
this->scanVariableDeclarationList(opt);
this->consumeSemicolon();
}
// ECMA-262 13.4 Empty Statement
template <typename T, bool isParse>
T emptyStatement()
{
this->expect(SemiColon);
if (isParse) {
MetaNode node = this->createNode();
return T(this->finalize(node, new EmptyStatementNode()));
}
return T(nullptr);
}
// ECMA-262 13.5 Expression Statement
PassRefPtr<ExpressionStatementNode> parseExpressionStatement()
{
MetaNode node = this->createNode();
RefPtr<Node> expr = this->expression<Parse>();
this->consumeSemicolon();
return this->finalize(node, new ExpressionStatementNode(expr.get()));
}
void scanExpressionStatement()
{
this->expression<Scan>();
this->consumeSemicolon();
}
// ECMA-262 13.6 If statement
template <typename T, bool isParse>
T ifStatement()
{
RefPtr<Node> test;
RefPtr<Node> consequent;
RefPtr<Node> alternate;
bool allowFunctionDeclaration = !this->context->strict;
this->expectKeyword(IfKeyword);
this->expect(LeftParenthesis);
if (isParse) {
test = this->expression<Parse>();
} else {
this->expression<Scan>();
}
this->expect(RightParenthesis);
if (isParse) {
consequent = this->parseStatement(allowFunctionDeclaration);
} else {
this->scanStatement(allowFunctionDeclaration);
}
if (this->matchKeyword(ElseKeyword)) {
this->nextToken();
if (isParse) {
alternate = this->parseStatement(allowFunctionDeclaration);
} else {
this->scanStatement(allowFunctionDeclaration);
}
}
if (isParse) {
return T(this->finalize(this->createNode(), new IfStatementNode(test.get(), consequent.get(), alternate.get())));
}
return T(nullptr);
}
// ECMA-262 13.7.2 The do-while Statement
template <typename T, bool isParse>
T doWhileStatement()
{
this->expectKeyword(DoKeyword);
bool previousInIteration = this->context->inIteration;
this->context->inIteration = true;
RefPtr<Node> body;
if (isParse) {
body = this->parseStatement(false);
} else {
this->scanStatement(false);
}
this->context->inIteration = previousInIteration;
this->expectKeyword(WhileKeyword);
this->expect(LeftParenthesis);
RefPtr<Node> test;
if (isParse) {
test = this->expression<Parse>();
} else {
this->expression<Scan>();
}
this->expect(RightParenthesis);
if (this->match(SemiColon)) {
this->nextToken();
}
if (isParse) {
return T(this->finalize(this->createNode(), new DoWhileStatementNode(test.get(), body.get())));
}
return T(nullptr);
}
// ECMA-262 13.7.3 The while Statement
template <typename T, bool isParse>
T whileStatement()
{
bool prevInLoop = this->context->inLoop;
this->context->inLoop = true;
this->expectKeyword(WhileKeyword);
this->expect(LeftParenthesis);
RefPtr<Node> test;
if (isParse) {
test = this->expression<Parse>();
} else {
this->expression<Scan>();
}
this->expect(RightParenthesis);
bool previousInIteration = this->context->inIteration;
this->context->inIteration = true;
RefPtr<Node> body;
if (isParse) {
body = this->parseStatement(false);
} else {
this->scanStatement(false);
}
this->context->inIteration = previousInIteration;
this->context->inLoop = prevInLoop;
if (isParse) {
return T(this->finalize(this->createNode(), new WhileStatementNode(test.get(), body.get())));
}
return T(nullptr);
}
// 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 <typename T, bool isParse>
T forStatement()
{
RefPtr<Node> init;
RefPtr<Node> test;
RefPtr<Node> update;
RefPtr<Node> left;
RefPtr<Node> right;
ForStatementType type = statementTypeFor;
bool prevInLoop = this->context->inLoop;
this->expectKeyword(ForKeyword);
this->expect(LeftParenthesis);
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;
VariableDeclaratorVector declarations = this->parseVariableDeclarationList(opt);
this->context->allowIn = previousAllowIn;
if (declarations.size() == 1 && this->matchKeyword(InKeyword)) {
RefPtr<VariableDeclaratorNode> decl = declarations[0];
// if (decl->init() && (decl.id.type === Syntax.ArrayPattern || decl.id.type === Syntax.ObjectPattern || this->context->strict)) {
if (decl->init() && (decl->id()->type() == ArrayExpression || decl->id()->type() == ObjectExpression || this->context->strict)) {
this->throwError(Messages::ForInOfLoopInitializer, new ASCIIString("for-in"));
}
if (isParse) {
left = this->finalize(this->createNode(), new VariableDeclarationNode(std::move(declarations) /*, 'var'*/));
}
this->nextToken();
type = statementTypeForIn;
} else if (declarations.size() == 1 && declarations[0]->init() == nullptr
&& this->lookahead.type == Token::IdentifierToken && this->lookahead.relatedSource() == "of") {
if (isParse) {
left = this->finalize(this->createNode(), new VariableDeclarationNode(std::move(declarations) /*, 'var'*/));
}
this->nextToken();
type = statementTypeForOf;
} else {
if (isParse) {
init = this->finalize(this->createNode(), new VariableDeclarationNode(std::move(declarations) /*, 'var'*/));
}
this->expect(SemiColon);
}
} else if (this->matchKeyword(ConstKeyword) || this->matchKeyword(LetKeyword)) {
// TODO
ALLOC_TOKEN(token);
this->nextToken(token);
this->throwUnexpectedToken(token);
/*
init = this->createNode();
const kind = this->nextToken().value;
if (!this->context->strict && this->lookahead.value === 'in') {
init = this->finalize(init, new Node.Identifier(kind));
this->nextToken();
left = init;
right = this->expression<Parse>();
init = null;
} else {
const previousAllowIn = this->context->allowIn;
this->context->allowIn = false;
const declarations = this->parseBindingList(kind, { inFor: true });
this->context->allowIn = previousAllowIn;
if (declarations.length === 1 && declarations[0].init === null && this->matchKeyword('in')) {
init = this->finalize(init, new Node.VariableDeclaration(declarations, kind));
this->nextToken();
left = init;
right = this->expression<Parse>();
init = null;
} else if (declarations.length === 1 && declarations[0].init === null && this->matchContextualKeyword('of')) {
init = this->finalize(init, new Node.VariableDeclaration(declarations, kind));
this->nextToken();
left = init;
right = this->assignmentExpression<Parse>();
init = null;
forIn = false;
} else {
this->consumeSemicolon();
init = this->finalize(init, new Node.VariableDeclaration(declarations, kind));
}
}
*/
} else {
ALLOC_TOKEN(initStartToken);
*initStartToken = this->lookahead;
bool previousAllowIn = this->context->allowIn;
this->context->allowIn = false;
init = this->inheritCoverGrammar(&Parser::assignmentExpression<Parse>);
this->context->allowIn = previousAllowIn;
if (this->matchKeyword(InKeyword)) {
if (init->isLiteral() || init->isAssignmentOperation() || init->type() == ASTNodeType::ThisExpression) {
this->throwError(Messages::InvalidLHSInForIn);
}
this->nextToken();
this->reinterpretExpressionAsPattern(init.get());
left = init;
init = nullptr;
type = statementTypeForIn;
} else if (this->lookahead.type == Token::IdentifierToken && this->lookahead.relatedSource() == "of") {
if (!this->context->isAssignmentTarget || init->isAssignmentOperation()) {
this->throwError(Messages::InvalidLHSInForLoop);
}
this->nextToken();
this->reinterpretExpressionAsPattern(init.get());
left = init;
init = nullptr;
type = statementTypeForOf;
} else {
if (this->match(Comma)) {
ExpressionNodeVector initSeq;
initSeq.push_back(init);
while (this->match(Comma)) {
this->nextToken();
initSeq.push_back(this->isolateCoverGrammar(&Parser::assignmentExpression<Parse>));
}
init = this->finalize(this->startNode(initStartToken), new SequenceExpressionNode(std::move(initSeq)));
}
this->expect(SemiColon);
}
}
}
if (type == statementTypeFor) {
this->context->inLoop = true;
if (!this->match(SemiColon)) {
test = this->expression<Parse>();
}
this->expect(SemiColon);
if (!this->match(RightParenthesis)) {
update = this->expression<Parse>();
}
} else if (type == statementTypeForIn) {
ASSERT(!isParse || left != nullptr);
if (isParse) {
right = this->expression<Parse>();
} else {
this->expression<Scan>();
}
} else {
ASSERT(type == statementTypeForOf);
ASSERT(!isParse || left != nullptr);
if (isParse) {
right = this->assignmentExpression<Parse>();
} else {
this->assignmentExpression<Scan>();
}
}
this->expect(RightParenthesis);
bool previousInIteration = this->context->inIteration;
this->context->inIteration = true;
RefPtr<Node> body;
if (isParse) {
auto functor = std::bind(&Parser::parseStatement, std::ref(*this), false);
body = this->isolateCoverGrammarWithFunctor(functor);
} else {
auto functor = std::bind(&Parser::scanStatement, std::ref(*this), false);
this->scanIsolateCoverGrammarWithFunctor(functor);
}
this->context->inIteration = previousInIteration;
this->context->inLoop = prevInLoop;
if (!isParse) {
return T(nullptr);
}
MetaNode node = this->createNode();
if (type == statementTypeFor) {
return T(this->finalize(node, new ForStatementNode(init.get(), test.get(), update.get(), body.get())));
}
if (type == statementTypeForIn) {
return T(this->finalize(node, new ForInOfStatementNode(left.get(), right.get(), body.get(), true)));
}
ASSERT(type == statementTypeForOf);
return T(this->finalize(node, new ForInOfStatementNode(left.get(), right.get(), body.get(), false)));
}
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 <typename T, bool isParse>
T continueStatement()
{
this->expectKeyword(ContinueKeyword);
AtomicString label;
if (this->lookahead.type == IdentifierToken && !this->hasLineTerminator) {
if (isParse) {
RefPtr<IdentifierNode> labelNode = this->parseVariableIdentifier();
label = labelNode->name();
} else {
this->scanVariableIdentifier();
label = lastScanIdentifierName;
}
if (!hasLabel(label)) {
this->throwError(Messages::UnknownLabel, label.string());
}
for (size_t i = 0; i < this->context->labelSet.size(); i++) {
if (this->context->labelSet[i].first == label && this->context->labelSet[i].second == 1) {
this->throwError(Messages::UnknownLabel, label.string());
}
}
} else if (!this->context->inIteration) {
this->throwError(Messages::IllegalContinue);
}
this->consumeSemicolon();
if (!isParse) {
return T(nullptr);
}
MetaNode node = this->createNode();
if (label.string()->length() != 0) {
return T(this->finalize(node, new ContinueLabelStatementNode(label.string())));
} else {
return T(this->finalize(node, new ContinueStatementNode()));
}
}
// ECMA-262 13.9 The break statement
template <typename T, bool isParse>
T breakStatement()
{
this->expectKeyword(BreakKeyword);
AtomicString label;
if (this->lookahead.type == IdentifierToken && !this->hasLineTerminator) {
if (isParse) {
RefPtr<IdentifierNode> labelNode = this->parseVariableIdentifier();
label = labelNode->name();
} else {
this->scanVariableIdentifier();
label = lastScanIdentifierName;
}
if (!hasLabel(label)) {
this->throwError(Messages::UnknownLabel, label.string());
}
} else if (!this->context->inIteration && !this->context->inSwitch) {
this->throwError(Messages::IllegalBreak);
}
this->consumeSemicolon();
if (!isParse) {
return T(nullptr);
}
MetaNode node = this->createNode();
if (label.string()->length() != 0) {
return T(this->finalize(node, new BreakLabelStatementNode(label.string())));
} else {
return T(this->finalize(node, new BreakStatementNode()));
}
}
// ECMA-262 13.10 The return statement
template <typename T, bool isParse>
T returnStatement()
{
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;
RefPtr<Node> argument;
if (hasArgument) {
if (isParse) {
argument = this->expression<Parse>();
} else {
this->expression<Scan>();
}
}
this->consumeSemicolon();
if (isParse) {
return T(this->finalize(this->createNode(), new ReturnStatmentNode(argument.get())));
}
return T(nullptr);
}
// ECMA-262 13.11 The with statement
PassRefPtr<Node> parseWithStatement()
{
if (this->context->strict) {
this->throwError(Messages::StrictModeWith);
}
this->expectKeyword(WithKeyword);
MetaNode node = this->createNode();
this->expect(LeftParenthesis);
RefPtr<Node> object = this->expression<Parse>();
this->expect(RightParenthesis);
bool prevInWith = this->context->inWith;
this->context->inWith = true;
for (size_t i = 0; i < this->context->labelSet.size(); i++) {
this->context->labelSet[i].second++;
}
RefPtr<StatementNode> body = this->parseStatement(false);
this->context->inWith = prevInWith;
for (size_t i = 0; i < this->context->labelSet.size(); i++) {
this->context->labelSet[i].second--;
}
return this->finalize(node, new WithStatementNode(object, body.get()));
}
// ECMA-262 13.12 The switch statement
PassRefPtr<SwitchCaseNode> parseSwitchCase()
{
MetaNode node = this->createNode();
RefPtr<Node> test;
if (this->matchKeyword(DefaultKeyword)) {
node = this->createNode();
this->nextToken();
test = nullptr;
} else {
this->expectKeyword(CaseKeyword);
node = this->createNode();
test = this->expression<Parse>();
}
this->expect(Colon);
RefPtr<StatementContainer> consequent = StatementContainer::create();
while (true) {
if (this->match(RightBrace) || this->matchKeyword(DefaultKeyword) || this->matchKeyword(CaseKeyword)) {
break;
}
consequent->appendChild(this->statementListItem<ParseAs(StatementNode)>());
}
return this->finalize(node, new SwitchCaseNode(test.get(), consequent.get()));
}
bool scanSwitchCase()
{
bool isDefaultNode;
if (this->matchKeyword(DefaultKeyword)) {
this->nextToken();
isDefaultNode = true;
} else {
this->expectKeyword(CaseKeyword);
this->expression<Scan>();
isDefaultNode = false;
}
this->expect(Colon);
while (true) {
if (this->match(RightBrace) || this->matchKeyword(DefaultKeyword) || this->matchKeyword(CaseKeyword)) {
break;
}
this->statementListItem<ScanAsVoid>();
}
return isDefaultNode;
}
template <typename T, bool isParse>
T switchStatement()
{
this->expectKeyword(SwitchKeyword);
this->expect(LeftParenthesis);
RefPtr<Node> discriminant;
if (isParse) {
discriminant = this->expression<Parse>();
} else {
this->expression<Scan>();
}
this->expect(RightParenthesis);
bool previousInSwitch = this->context->inSwitch;
this->context->inSwitch = true;
RefPtr<StatementContainer> casesA, casesB;
RefPtr<Node> deflt;
if (isParse) {
casesA = StatementContainer::create();
casesB = StatementContainer::create();
}
bool defaultFound = false;
this->expect(LeftBrace);
while (true) {
if (this->match(RightBrace)) {
break;
}
if (isParse) {
RefPtr<SwitchCaseNode> clause = this->parseSwitchCase();
if (clause->isDefaultNode()) {
if (defaultFound) {
this->throwError(Messages::MultipleDefaultsInSwitch);
}
deflt = clause;
defaultFound = true;
} else {
if (defaultFound) {
casesA->appendChild(clause);
} else {
casesB->appendChild(clause);
}
}
} else if (this->scanSwitchCase()) {
if (defaultFound) {
this->throwError(Messages::MultipleDefaultsInSwitch);
}
defaultFound = true;
}
}
this->expect(RightBrace);
this->context->inSwitch = previousInSwitch;
if (isParse) {
return T(this->finalize(this->createNode(), new SwitchStatementNode(discriminant.get(), casesA.get(), deflt.get(), casesB.get(), false)));
}
return T(nullptr);
}
// ECMA-262 13.13 Labelled Statements
template <typename T, bool isParse>
T labelledStatement()
{
RefPtr<Node> expr;
AtomicString name;
bool isIdentifier = false;
if (isParse) {
expr = this->expression<Parse>();
if (expr->type() == Identifier) {
isIdentifier = true;
name = ((IdentifierNode*)expr.get())->name();
}
} else {
ScanExpressionResult result = this->expression<Scan>();
if (result == Identifier) {
isIdentifier = true;
name = lastScanIdentifierName;
}
}
StatementNode* 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, 0));
if (isParse) {
RefPtr<StatementNode> labeledBody = this->parseStatement(!this->context->strict);
statement = new LabeledStatementNode(labeledBody.get(), name.string());
} else {
this->scanStatement(!this->context->strict);
}
removeLabel(name);
} else {
this->consumeSemicolon();
if (isParse) {
statement = new ExpressionStatementNode(expr.get());
}
}
if (isParse) {
return T(this->finalize(this->createNode(), statement));
}
return T(nullptr);
}
// ECMA-262 13.14 The throw statement
template <typename T, bool isParse>
T throwStatement()
{
this->expectKeyword(ThrowKeyword);
if (this->hasLineTerminator) {
this->throwError(Messages::NewlineAfterThrow);
}
RefPtr<Node> argument;
if (isParse) {
argument = this->expression<Parse>();
} else {
this->expression<Scan>();
}
this->consumeSemicolon();
if (isParse) {
return T(this->finalize(this->createNode(), new ThrowStatementNode(argument.get())));
}
return T(nullptr);
}
// ECMA-262 13.15 The try statement
template <typename T, bool isParse>
T catchClause()
{
this->expectKeyword(CatchKeyword);
this->expect(LeftParenthesis);
if (this->match(RightParenthesis)) {
this->throwUnexpectedToken(&this->lookahead);
}
bool prevInCatch = this->context->inCatch;
this->context->inCatch = true;
std::vector<FunctionDeclarationNode*> vecBefore = std::move(this->context->functionDeclarationsInDirectCatchScope);
bool prevInDirectCatchScope = this->context->inDirectCatchScope;
this->context->inDirectCatchScope = true;
Vector<Scanner::ScannerResult, GCUtil::gc_malloc_ignore_off_page_allocator<Scanner::ScannerResult>> params;
RefPtr<Node> param = this->pattern<Parse>(params);
std::vector<String*, GCUtil::gc_malloc_ignore_off_page_allocator<String*>> paramMap;
for (size_t i = 0; i < params.size(); i++) {
for (size_t j = 0; j < paramMap.size(); j++) {
StringView* sv = params[i].valueStringLiteral();
if (paramMap[j]->equals(sv)) {
this->throwError(Messages::DuplicateBinding, new StringView(params[i].relatedSource()));
}
}
paramMap.push_back(new StringView(params[i].relatedSource()));
}
if (this->context->strict && param->type() == Identifier) {
IdentifierNode* id = (IdentifierNode*)param.get();
if (this->scanner->isRestrictedWord(id->name())) {
this->throwError(Messages::StrictCatchVariable);
}
}
this->expect(RightParenthesis);
RefPtr<Node> body;
if (isParse) {
body = this->block<Parse>();
} else {
this->block<ScanAsVoid>();
}
this->context->inCatch = prevInCatch;
this->context->inDirectCatchScope = prevInDirectCatchScope;
std::vector<FunctionDeclarationNode*> vec = std::move(this->context->functionDeclarationsInDirectCatchScope);
this->context->functionDeclarationsInDirectCatchScope = std::move(vecBefore);
if (isParse) {
return T(this->finalize(this->createNode(), new CatchClauseNode(param.get(), nullptr, body.get(), vec)));
}
scopeContexts.back()->m_hasCatch = true;
return T(nullptr);
}
template <typename T, bool isParse>
T finallyClause()
{
this->expectKeyword(FinallyKeyword);
if (isParse) {
return T(this->block<ParseAs(BlockStatementNode)>());
}
return T(nullptr);
}
PassRefPtr<TryStatementNode> parseTryStatement()
{
this->expectKeyword(TryKeyword);
RefPtr<BlockStatementNode> block = this->block<ParseAs(BlockStatementNode)>();
RefPtr<CatchClauseNode> handler;
if (this->matchKeyword(CatchKeyword)) {
handler = this->catchClause<ParseAs(CatchClauseNode)>();
}
RefPtr<BlockStatementNode> finalizer;
if (this->matchKeyword(FinallyKeyword)) {
finalizer = this->finallyClause<ParseAs(BlockStatementNode)>();
}
if (!handler && !finalizer) {
this->throwError(Messages::NoCatchOrFinally);
}
return this->finalize(this->createNode(), new TryStatementNode(block.get(), handler.get(), CatchClauseNodeVector(), finalizer.get()));
}
void scanTryStatement()
{
this->expectKeyword(TryKeyword);
this->block<ScanAsVoid>();
bool meetHandler = this->matchKeyword(CatchKeyword);
if (meetHandler) {
this->catchClause<ScanAsVoid>();
}
bool meetFinalizer = this->matchKeyword(FinallyKeyword);
if (meetFinalizer) {
this->finallyClause<ScanAsVoid>();
}
if (!meetHandler && !meetFinalizer) {
this->throwError(Messages::NoCatchOrFinally);
}
}
// ECMA-262 13.16 The debugger statement
PassRefPtr<StatementNode> parseDebuggerStatement()
{
this->throwError("debugger keyword is not supported yet");
RELEASE_ASSERT_NOT_REACHED();
/*
const node = this->createNode();
this->expectKeyword('debugger');
this->consumeSemicolon();
return this->finalize(node, new Node.DebuggerStatement());
*/
}
// ECMA-262 13 Statements
PassRefPtr<StatementNode> parseStatement(bool allowFunctionDeclaration = true)
{
checkRecursiveLimit();
RefPtr<StatementNode> statement;
switch (this->lookahead.type) {
case Token::BooleanLiteralToken:
case Token::NullLiteralToken:
case Token::NumericLiteralToken:
case Token::StringLiteralToken:
case Token::TemplateToken:
case Token::RegularExpressionToken:
statement = this->parseExpressionStatement();
break;
case Token::PunctuatorToken: {
PunctuatorKind value = this->lookahead.valuePunctuatorKind;
if (value == LeftBrace) {
statement = this->block<ParseAs(BlockStatementNode)>();
} else if (value == LeftParenthesis) {
statement = this->parseExpressionStatement();
} else if (value == SemiColon) {
statement = this->emptyStatement<ParseAs(StatementNode)>();
} else {
statement = this->parseExpressionStatement();
}
break;
}
case Token::IdentifierToken:
statement = this->labelledStatement<ParseAs(StatementNode)>();
break;
case Token::KeywordToken:
switch (this->lookahead.valueKeywordKind) {
case BreakKeyword:
statement = this->breakStatement<ParseAs(StatementNode)>();
break;
case ContinueKeyword:
statement = this->continueStatement<ParseAs(StatementNode)>();
break;
case DebuggerKeyword:
statement = asStatementNode(this->parseDebuggerStatement());
break;
case DoKeyword:
statement = this->doWhileStatement<ParseAs(StatementNode)>();
break;
case ForKeyword:
statement = this->forStatement<ParseAs(StatementNode)>();
break;
case FunctionKeyword: {
if (!allowFunctionDeclaration) {
this->throwUnexpectedToken(&this->lookahead);
}
statement = asStatementNode(this->parseFunctionDeclaration());
break;
}
case IfKeyword:
statement = this->ifStatement<ParseAs(StatementNode)>();
break;
case ReturnKeyword:
statement = this->returnStatement<ParseAs(StatementNode)>();
break;
case SwitchKeyword:
statement = this->switchStatement<ParseAs(StatementNode)>();
break;
case ThrowKeyword:
statement = this->throwStatement<ParseAs(StatementNode)>();
break;
case TryKeyword:
statement = asStatementNode(this->parseTryStatement());
break;
case VarKeyword:
statement = asStatementNode(this->parseVariableStatement());
break;
case WhileKeyword:
statement = this->whileStatement<ParseAs(StatementNode)>();
break;
case WithKeyword:
statement = asStatementNode(this->parseWithStatement());
break;
case ClassKeyword:
statement = asStatementNode(this->parseClassDeclaration());
break;
default:
statement = asStatementNode(this->parseExpressionStatement());
break;
}
break;
default:
this->throwUnexpectedToken(&this->lookahead);
}
return statement;
}
void scanStatement(bool allowFunctionDeclaration = true)
{
checkRecursiveLimit();
switch (this->lookahead.type) {
case Token::BooleanLiteralToken:
case Token::NullLiteralToken:
case Token::NumericLiteralToken:
case Token::StringLiteralToken:
case Token::TemplateToken:
case Token::RegularExpressionToken:
this->scanExpressionStatement();
break;
case Token::PunctuatorToken: {
PunctuatorKind value = this->lookahead.valuePunctuatorKind;
if (value == LeftBrace) {
this->block<ScanAsVoid>();
} else if (value == LeftParenthesis) {
this->scanExpressionStatement();
} else if (value == SemiColon) {
this->emptyStatement<ScanAsVoid>();
} else {
this->scanExpressionStatement();
}
break;
}
case Token::IdentifierToken:
this->labelledStatement<ScanAsVoid>();
break;
case Token::KeywordToken:
switch (this->lookahead.valueKeywordKind) {
case BreakKeyword:
this->breakStatement<ScanAsVoid>();
break;
case ContinueKeyword:
this->continueStatement<ScanAsVoid>();
break;
case DebuggerKeyword:
this->parseDebuggerStatement();
break;
case DoKeyword:
this->doWhileStatement<ScanAsVoid>();
break;
case ForKeyword:
this->forStatement<ScanAsVoid>();
break;
case FunctionKeyword: {
if (!allowFunctionDeclaration) {
this->throwUnexpectedToken(&this->lookahead);
}
this->parseFunctionDeclaration();
break;
}
case IfKeyword:
this->ifStatement<ScanAsVoid>();
break;
case ReturnKeyword:
this->returnStatement<ScanAsVoid>();
break;
case SwitchKeyword:
this->switchStatement<ScanAsVoid>();
break;
case ThrowKeyword:
this->throwStatement<ScanAsVoid>();
break;
case TryKeyword:
this->scanTryStatement();
break;
case VarKeyword:
this->scanVariableStatement();
break;
case WhileKeyword:
this->whileStatement<ScanAsVoid>();
break;
case WithKeyword:
this->parseWithStatement();
break;
default:
this->scanExpressionStatement();
break;
}
break;
default:
this->throwUnexpectedToken(&this->lookahead);
}
}
PassRefPtr<Node> parseArrowFunctionSourceElements()
{
ASSERT(this->config.parseSingleFunction);
RefPtr<StatementContainer> argumentInitializers = nullptr;
if (this->config.reparseArguments) {
this->reparseFunctionArguments(argumentInitializers);
}
this->context->inArrowFunction = true;
if (this->match(LeftBrace)) {
return parseFunctionSourceElements();
}
bool prevInDirectCatchScope = this->context->inDirectCatchScope;
this->context->inDirectCatchScope = false;
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;
this->expect(Arrow);
MetaNode nodeStart = this->createNode();
RefPtr<Node> expr = this->isolateCoverGrammar(&Parser::assignmentExpression<Parse>);
RefPtr<StatementContainer> body = StatementContainer::create();
body->appendChild(this->finalize(nodeStart, new ReturnStatmentNode(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;
this->context->inDirectCatchScope = prevInDirectCatchScope;
return this->finalize(nodeStart, new BlockStatementNode(body.get(), argumentInitializers.get()));
}
// ECMA-262 14.1 Function Definition
void reparseFunctionArguments(RefPtr<StatementContainer>& argumentInitializers)
{
InterpretedCodeBlock* currentTarget = this->config.parseSingleFunctionTarget->asInterpretedCodeBlock();
Scanner ParamsScannerInstance(this->escargotContext, currentTarget->paramsSrc(), 0, 0);
this->scanner = &ParamsScannerInstance;
this->setMarkers(ExtendedNodeLOC(0, 0, 0));
argumentInitializers = StatementContainer::create();
this->expect(LeftParenthesis);
ParseParameterOptions options;
size_t i = 0;
while (!this->match(RightParenthesis)) {
bool end = !this->parseFormalParameter(options);
RefPtr<Node> param = options.params.back();
if (!param->isIdentifier()) {
MetaNode node = this->createNode();
RefPtr<Node> p;
if (param->isPattern()) {
RefPtr<IdentifierNode> id = adoptRef(new IdentifierNode(this->createPatternName(i)));
RefPtr<AssignmentExpressionSimpleNode> assign = adoptRef(new AssignmentExpressionSimpleNode(param.get(), id.get()));
p = assign;
} else {
p = param.get();
}
RefPtr<Node> statement = this->finalize(node, new ExpressionStatementNode(p.get()));
argumentInitializers->appendChild(statement->asStatementNode());
}
if (end) {
break;
}
i++;
this->expect(Comma);
}
this->expect(RightParenthesis);
this->scanner = &this->scannerInstance;
this->scanner->index = 0;
this->scanner->lineNumber = currentTarget->sourceElementStart().line;
this->scanner->lineStart = currentTarget->sourceElementStart().column;
this->setMarkers(currentTarget->sourceElementStart());
}
PassRefPtr<Node> parseFunctionSourceElements()
{
RefPtr<StatementContainer> argumentInitializers = nullptr;
if (this->config.parseSingleFunction) {
if (this->config.parseSingleFunctionChildIndex > 0) {
size_t realIndex = this->config.parseSingleFunctionChildIndex - 1;
this->config.parseSingleFunctionChildIndex++;
InterpretedCodeBlock* currentTarget = this->config.parseSingleFunctionTarget->asInterpretedCodeBlock();
size_t orgIndex = this->lookahead.end;
this->expect(LeftBrace);
StringView src = currentTarget->childBlocks()[realIndex]->src();
this->scanner->index = src.length() + orgIndex;
this->scanner->lineNumber = currentTarget->childBlocks()[realIndex]->sourceElementStart().line;
this->scanner->lineStart = currentTarget->childBlocks()[realIndex]->sourceElementStart().index - currentTarget->childBlocks()[realIndex]->sourceElementStart().column;
this->lookahead.lineNumber = this->scanner->lineNumber;
this->lookahead.lineNumber = this->scanner->lineStart;
this->lookahead.type = Token::PunctuatorToken;
this->lookahead.valuePunctuatorKind = PunctuatorKind::RightBrace;
this->expect(RightBrace);
return this->finalize(this->createNode(), new BlockStatementNode(StatementContainer::create().get()));
}
this->config.parseSingleFunctionChildIndex++;
if (this->config.reparseArguments) {
this->reparseFunctionArguments(argumentInitializers);
}
}
bool prevInDirectCatchScope = this->context->inDirectCatchScope;
this->context->inDirectCatchScope = false;
MetaNode nodeStart = this->createNode();
this->expect(LeftBrace);
RefPtr<StatementContainer> body = this->parseDirectivePrologues();
auto previousLabelSet = this->context->labelSet;
bool previousInIteration = this->context->inIteration;
bool previousInSwitch = this->context->inSwitch;
bool previousInFunctionBody = this->context->inFunctionBody;
this->context->labelSet.clear();
this->context->inIteration = false;
this->context->inSwitch = false;
this->context->inFunctionBody = true;
if (shouldCreateAST()) {
StatementNode* referNode = nullptr;
while (this->startMarker.index < this->scanner->length) {
if (this->match(RightBrace)) {
break;
}
referNode = body->appendChild(this->statementListItem<ParseAs(StatementNode)>().get(), referNode);
}
} else {
StatementNode* referNode = nullptr;
while (this->startMarker.index < this->scanner->length) {
if (this->match(RightBrace)) {
break;
}
this->statementListItem<ScanAsVoid>();
}
}
this->expect(RightBrace);
this->context->labelSet = previousLabelSet;
this->context->inIteration = previousInIteration;
this->context->inSwitch = previousInSwitch;
this->context->inFunctionBody = previousInFunctionBody;
scopeContexts.back()->m_locStart.line = nodeStart.line;
scopeContexts.back()->m_locStart.column = nodeStart.column;
scopeContexts.back()->m_locStart.index = nodeStart.index;
scopeContexts.back()->m_locEnd.index = this->lastMarker.index;
#ifndef NDEBUG
scopeContexts.back()->m_locEnd.line = this->lastMarker.lineNumber;
scopeContexts.back()->m_locEnd.column = this->lastMarker.index - this->lastMarker.lineStart;
#endif
this->context->inDirectCatchScope = prevInDirectCatchScope;
if (this->config.parseSingleFunction) {
return this->finalize(nodeStart, new BlockStatementNode(body.get(), argumentInitializers.get()));
} else {
return this->finalize(nodeStart, new BlockStatementNode(StatementContainer::create().get()));
}
}
template <class FunctionType, bool isFunctionDeclaration>
PassRefPtr<FunctionType> parseFunction(MetaNode node)
{
this->expectKeyword(FunctionKeyword);
bool isGenerator = this->match(Multiply);
if (isGenerator) {
this->nextToken();
}
const char* message = nullptr;
RefPtr<IdentifierNode> id;
Scanner::ScannerResult firstRestricted;
bool previousAllowYield = this->context->allowYield;
bool previousInArrowFunction = this->context->inArrowFunction;
this->context->allowYield = !isGenerator;
this->context->inArrowFunction = false;
if (isFunctionDeclaration || !this->match(LeftParenthesis)) {
ALLOC_TOKEN(token);
*token = this->lookahead;
id = (!isFunctionDeclaration && !this->context->strict && !isGenerator && this->matchKeyword(YieldKeyword)) ? this->parseIdentifierName() : this->parseVariableIdentifier();
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->name() : AtomicString();
if (!isFunctionDeclaration) {
pushScopeContext(fnName);
}
if (id) {
scopeContexts.back()->insertName(fnName, isFunctionDeclaration);
insertUsingName(fnName);
}
if (isFunctionDeclaration) {
pushScopeContext(fnName);
}
scopeContexts.back()->m_paramsStart.index = paramsStart.index;
ParseFormalParametersResult formalParameters = this->parseFormalParameters(&firstRestricted);
PatternNodeVector params = std::move(formalParameters.params);
Scanner::ScannerResult stricted = formalParameters.stricted;
firstRestricted = formalParameters.firstRestricted;
if (formalParameters.message) {
message = formalParameters.message;
}
extractNamesFromFunctionParams(params);
bool previousStrict = this->context->strict;
RefPtr<Node> body = this->parseFunctionSourceElements();
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;
if (isFunctionDeclaration && this->context->inDirectCatchScope) {
scopeContexts.back()->m_needsSpecialInitialize = true;
}
return this->finalize(node, new FunctionType(fnName, std::move(params), body.get(), popScopeContext(node), isGenerator));
}
PassRefPtr<FunctionDeclarationNode> parseFunctionDeclaration()
{
MetaNode node = this->createNode();
RefPtr<FunctionDeclarationNode> fd = parseFunction<FunctionDeclarationNode, true>(node);
if (this->context->inDirectCatchScope) {
this->context->functionDeclarationsInDirectCatchScope.push_back(fd.get());
}
return fd;
}
PassRefPtr<FunctionExpressionNode> parseFunctionExpression()
{
MetaNode node = this->createNode();
return parseFunction<FunctionExpressionNode, false>(node);
}
// ECMA-262 14.1.1 Directive Prologues
PassRefPtr<Node> parseDirective()
{
ALLOC_TOKEN(token);
*token = this->lookahead;
StringView* directiveValue;
bool isLiteral = false;
MetaNode node = this->createNode();
RefPtr<Node> expr = this->expression<Parse>();
if (expr->type() == Literal) {
isLiteral = true;
directiveValue = token->valueStringLiteral();
}
this->consumeSemicolon();
if (isLiteral) {
return this->finalize(node, new DirectiveNode(asExpressionNode(expr).get(), *directiveValue));
} else {
return this->finalize(node, new ExpressionStatementNode(expr.get()));
}
}
PassRefPtr<StatementContainer> parseDirectivePrologues()
{
Scanner::ScannerResult firstRestricted;
this->context->inParsingDirective = true;
RefPtr<StatementContainer> body = StatementContainer::create();
ALLOC_TOKEN(token);
while (true) {
*token = this->lookahead;
if (token->type != StringLiteralToken) {
break;
}
RefPtr<Node> statement = this->parseDirective();
body->appendChild(statement->asStatementNode());
if (statement->type() != Directive) {
break;
}
DirectiveNode* directive = (DirectiveNode*)statement.get();
if (token->plain && directive->value().equals("use strict")) {
this->scopeContexts.back()->m_isStrict = this->context->strict = true;
if (firstRestricted) {
this->throwUnexpectedToken(&firstRestricted, Messages::StrictOctalLiteral);
}
} else {
if (!firstRestricted && token->octal) {
firstRestricted = *token;
}
}
}
this->context->inParsingDirective = false;
return body;
}
// ECMA-262 14.3 Method Definitions
PassRefPtr<FunctionExpressionNode> parseGetterMethod()
{
MetaNode node = this->createNode();
this->expect(LeftParenthesis);
this->expect(RightParenthesis);
bool isGenerator = false;
ParseFormalParametersResult params(PatternNodeVector(), nullptr);
bool previousAllowYield = this->context->allowYield;
this->context->allowYield = false;
RefPtr<Node> method = this->parsePropertyMethod(params);
this->context->allowYield = previousAllowYield;
extractNamesFromFunctionParams(params.params);
scopeContexts.back()->m_paramsStart.index = node.index;
return this->finalize(node, new FunctionExpressionNode(AtomicString(), std::move(params.params), method.get(), popScopeContext(node), isGenerator));
}
PassRefPtr<FunctionExpressionNode> parseSetterMethod()
{
MetaNode node = this->createNode();
ParseParameterOptions options;
bool isGenerator = false;
bool previousAllowYield = this->context->allowYield;
this->context->allowYield = false;
this->expect(LeftParenthesis);
pushScopeContext(AtomicString());
if (this->match(RightParenthesis)) {
this->throwUnexpectedToken(&this->lookahead);
} else {
this->parseFormalParameter(options);
}
this->expect(RightParenthesis);
bool previousInArrowFunction = this->context->inArrowFunction;
this->context->inArrowFunction = false;
this->context->isAssignmentTarget = false;
this->context->isBindingElement = false;
extractNamesFromFunctionParams(options.params);
scopeContexts.back()->m_paramsStart.index = node.index;
const bool previousStrict = this->context->strict;
PassRefPtr<Node> method = this->isolateCoverGrammar(&Parser::parseFunctionSourceElements);
if (this->context->strict && options.firstRestricted) {
this->throwUnexpectedToken(&options.firstRestricted, options.message);
}
if (this->context->strict && options.stricted) {
this->throwUnexpectedToken(&options.stricted, options.message);
}
this->context->strict = previousStrict;
this->context->inArrowFunction = previousInArrowFunction;
this->context->allowYield = previousAllowYield;
return this->finalize(node, new FunctionExpressionNode(AtomicString(), std::move(options.params), method.get(), popScopeContext(node), isGenerator));
}
PassRefPtr<FunctionExpressionNode> parseGeneratorMethod()
{
MetaNode node = this->createNode();
const bool previousAllowYield = this->context->allowYield;
this->expect(LeftParenthesis);
pushScopeContext(AtomicString());
this->context->allowYield = true;
ParseFormalParametersResult formalParameters = this->parseFormalParameters();
this->context->allowYield = false;
// Note: Change it to parsePropertyMethod, if possible
RefPtr<Node> method = this->isolateCoverGrammar(&Parser::parseFunctionSourceElements);
this->context->allowYield = previousAllowYield;
extractNamesFromFunctionParams(formalParameters.params);
scopeContexts.back()->m_paramsStart.index = node.index;
return this->finalize(node, new FunctionExpressionNode(AtomicString(), std::move(formalParameters.params), method.get(), popScopeContext(node), true));
}
// ECMA-262 14.4 Generator Function Definitions
template <typename T, bool isParse>
T yieldExpression()
{
MetaNode node = this->createNode();
this->expectKeyword(YieldKeyword);
RefPtr<Node> 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();
if (isParse) {
exprNode = this->assignmentExpression<Parse>();
} else {
this->assignmentExpression<Scan>();
}
} else {
if (!this->match(SemiColon) && !this->match(RightBrace) && !this->match(RightParenthesis) && this->lookahead.type != Token::EOFToken) {
if (isParse) {
exprNode = this->assignmentExpression<Parse>();
} else {
this->assignmentExpression<Scan>();
}
}
}
this->context->allowYield = previousAllowYield;
}
if (isParse) {
return this->finalize(node, new YieldExpressionNode(exprNode, delegate));
}
return ScanExpressionResult(ASTNodeType::YieldExpression);
}
// ECMA-262 14.5 Class Definitions
PassRefPtr<ClassElementNode> parseClassElement(RefPtr<FunctionExpressionNode>* constructor)
{
ALLOC_TOKEN(token);
*token = this->lookahead;
MetaNode node = this->createNode();
ClassElementNode::Kind kind = ClassElementNode::Kind::None;
RefPtr<Node> key;
RefPtr<FunctionExpressionNode> value;
bool computed = false;
bool isStatic = false;
if (this->match(Multiply)) {
this->nextToken();
} else {
computed = this->match(LeftSquareBracket);
key = this->parseObjectPropertyKey();
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 {
key = this->parseObjectPropertyKey();
}
}
}
bool lookaheadPropertyKey = this->qualifiedPropertyName(&this->lookahead);
if (token->type == Token::IdentifierToken) {
if (*token->valueStringLiteral() == "get" && lookaheadPropertyKey) {
kind = ClassElementNode::Kind::Get;
computed = this->match(LeftSquareBracket);
key = this->parseObjectPropertyKey();
this->context->allowYield = false;
value = this->parseGetterMethod();
} else if (*token->valueStringLiteral() == "set" && lookaheadPropertyKey) {
kind = ClassElementNode::Kind::Set;
computed = this->match(LeftSquareBracket);
key = this->parseObjectPropertyKey();
value = this->parseSetterMethod();
}
} else if (lookaheadPropertyKey && token->type == Token::PunctuatorToken && token->valuePunctuatorKind == Multiply) {
kind = ClassElementNode::Kind::Method;
computed = this->match(LeftSquareBracket);
key = this->parseObjectPropertyKey();
value = this->parseGeneratorMethod();
}
if (kind == ClassElementNode::Kind::None && key && this->match(LeftParenthesis)) {
kind = ClassElementNode::Kind::Method;
value = this->parsePropertyMethodFunction();
}
if (kind == ClassElementNode::Kind::None) {
this->throwUnexpectedToken(&this->lookahead);
}
if (!computed) {
if (isStatic && this->isPropertyKey(key.get(), "prototype")) {
this->throwUnexpectedToken(token, Messages::StaticPrototype);
}
if (!isStatic && this->isPropertyKey(key.get(), "constructor")) {
if (kind != ClassElementNode::Kind::Method || value->function().isGenerator()) {
this->throwUnexpectedToken(token, Messages::ConstructorSpecialMethod);
}
if (*constructor != nullptr) {
this->throwUnexpectedToken(token, Messages::DuplicateConstructor);
} else {
*constructor = value;
return nullptr;
}
}
}
return this->finalize(node, new ClassElementNode(key.get(), value.get(), kind, computed, isStatic));
}
PassRefPtr<ClassBodyNode> parseClassBody()
{
MetaNode node = this->createNode();
ClassElementNodeVector body;
RefPtr<FunctionExpressionNode> constructor = nullptr;
this->expect(LeftBrace);
while (!this->match(RightBrace)) {
if (this->match(SemiColon)) {
this->nextToken();
} else {
PassRefPtr<ClassElementNode> classElement = this->parseClassElement(&constructor);
if (classElement != nullptr) {
body.push_back(classElement);
}
}
}
this->expect(RightBrace);
return this->finalize(node, new ClassBodyNode(std::move(body), constructor));
}
template <class ClassType>
PassRefPtr<ClassType> parseClass(bool identifierIsOptional)
{
bool previousStrict = this->context->strict;
this->context->strict = true;
this->expectKeyword(ClassKeyword);
MetaNode node = this->createNode();
RefPtr<IdentifierNode> id = (identifierIsOptional && this->lookahead.type != Token::IdentifierToken) ? nullptr : this->parseVariableIdentifier();
if (id) {
scopeContexts.back()->insertName(id->name(), true);
insertUsingName(id->name());
}
RefPtr<Node> superClass = nullptr;
if (this->matchKeyword(ExtendsKeyword)) {
this->nextToken();
superClass = this->isolateCoverGrammar(&Parser::leftHandSideExpressionAllowCall<Parse>);
}
RefPtr<ClassBodyNode> classBody = this->parseClassBody();
this->context->strict = previousStrict;
return this->finalize(node, new ClassType(id, superClass, classBody.get()));
}
PassRefPtr<ClassDeclarationNode> parseClassDeclaration()
{
return parseClass<ClassDeclarationNode>(false);
}
PassRefPtr<ClassExpressionNode> parseClassExpression()
{
return parseClass<ClassExpressionNode>(true);
}
// ECMA-262 15.1 Scripts
// ECMA-262 15.2 Modules
PassRefPtr<ProgramNode> parseProgram()
{
MetaNode node = this->createNode();
pushScopeContext(new ASTScopeContext(this->context->strict));
RefPtr<StatementContainer> body = this->parseDirectivePrologues();
StatementNode* referNode = nullptr;
while (this->startMarker.index < this->scanner->length) {
referNode = body->appendChild(this->statementListItem<ParseAs(StatementNode)>().get(), referNode);
}
scopeContexts.back()->m_locStart.line = node.line;
scopeContexts.back()->m_locStart.column = node.column;
scopeContexts.back()->m_locStart.index = node.index;
MetaNode endNode = this->createNode();
#ifndef NDEBUG
scopeContexts.back()->m_locEnd.line = endNode.line;
scopeContexts.back()->m_locEnd.column = endNode.column;
#endif
scopeContexts.back()->m_locEnd.index = endNode.index;
return this->finalize(node, new ProgramNode(body.get(), scopeContexts.back() /*, this->sourceType*/));
}
// ECMA-262 15.2.2 Imports
/*
parseModuleSpecifier(): Node.Literal {
const node = this.createNode();
if (this.lookahead.type !== Token.StringLiteral) {
this.throwError(Messages.InvalidModuleSpecifier);
}
const token = this.nextToken();
const raw = this.getTokenRaw(token);
return this.finalize(node, new Node.Literal(token.value, raw));
}
// import {<foo as bar>} ...;
parseImportSpecifier(): Node.ImportSpecifier {
const node = this.createNode();
let local;
const imported = this.parseIdentifierName();
if (this.matchContextualKeyword('as')) {
this.nextToken();
local = this.parseVariableIdentifier();
} else {
local = imported;
}
return this.finalize(node, new Node.ImportSpecifier(local, imported));
}
// {foo, bar as bas}
parseNamedImports(): Node.ImportSpecifier[] {
this.expect('{');
const specifiers: Node.ImportSpecifier[] = [];
while (!this.match('}')) {
specifiers.push(this.parseImportSpecifier());
if (!this.match('}')) {
this.expect(',');
}
}
this.expect('}');
return specifiers;
}
// import <foo> ...;
parseImportDefaultSpecifier(): Node.ImportDefaultSpecifier {
const node = this.createNode();
const local = this.parseIdentifierName();
return this.finalize(node, new Node.ImportDefaultSpecifier(local));
}
// import <* as foo> ...;
parseImportNamespaceSpecifier(): Node.ImportNamespaceSpecifier {
const node = this.createNode();
this.expect('*');
if (!this.matchContextualKeyword('as')) {
this.throwError(Messages.NoAsAfterImportNamespace);
}
this.nextToken();
const local = this.parseIdentifierName();
return this.finalize(node, new Node.ImportNamespaceSpecifier(local));
}
parseImportDeclaration(): Node.ImportDeclaration {
if (this.context.inFunctionBody) {
this.throwError(Messages.IllegalImportDeclaration);
}
const node = this.createNode();
this.expectKeyword('import');
let src: Node.Literal;
let specifiers: Node.ImportDeclarationSpecifier[] = [];
if (this.lookahead.type === Token.StringLiteral) {
// import 'foo';
src = this.parseModuleSpecifier();
} else {
if (this.match('{')) {
// import {bar}
specifiers = specifiers.concat(this.parseNamedImports());
} else if (this.match('*')) {
// import * as foo
specifiers.push(this.parseImportNamespaceSpecifier());
} else if (this.isIdentifierName(this.lookahead) && !this.matchKeyword('default')) {
// import foo
specifiers.push(this.parseImportDefaultSpecifier());
if (this.match(',')) {
this.nextToken();
if (this.match('*')) {
// import foo, * as foo
specifiers.push(this.parseImportNamespaceSpecifier());
} else if (this.match('{')) {
// import foo, {bar}
specifiers = specifiers.concat(this.parseNamedImports());
} else {
this.throwUnexpectedToken(this.lookahead);
}
}
} else {
this.throwUnexpectedToken(this.nextToken());
}
if (!this.matchContextualKeyword('from')) {
const message = this.lookahead.value ? Messages.UnexpectedToken : Messages.MissingFromClause;
this.throwError(message, this.lookahead.value);
}
this.nextToken();
src = this.parseModuleSpecifier();
}
this.consumeSemicolon();
return this.finalize(node, new Node.ImportDeclaration(specifiers, src));
}
// ECMA-262 15.2.3 Exports
parseExportSpecifier(): Node.ExportSpecifier {
const node = this.createNode();
const local = this.parseIdentifierName();
let exported = local;
if (this.matchContextualKeyword('as')) {
this.nextToken();
exported = this.parseIdentifierName();
}
return this.finalize(node, new Node.ExportSpecifier(local, exported));
}
parseExportDeclaration(): Node.ExportDeclaration {
if (this.context.inFunctionBody) {
this.throwError(Messages.IllegalExportDeclaration);
}
const node = this.createNode();
this.expectKeyword('export');
let exportDeclaration;
if (this.matchKeyword('default')) {
// export default ...
this.nextToken();
if (this.matchKeyword('function')) {
// export default function foo () {}
// export default function () {}
const declaration = this.parseFunctionDeclaration(true);
exportDeclaration = this.finalize(node, new Node.ExportDefaultDeclaration(declaration));
} else if (this.matchKeyword('class')) {
// export default class foo {}
const declaration = this.parseClassDeclaration(true);
exportDeclaration = this.finalize(node, new Node.ExportDefaultDeclaration(declaration));
} else {
if (this.matchContextualKeyword('from')) {
this.throwError(Messages.UnexpectedToken, this.lookahead.value);
}
// export default {};
// export default [];
// export default (1 + 2);
const declaration = this.match('{') ? this.parseObjectInitializer() :
this.match('[') ? this.parseArrayInitializer() : this.assignmentExpression<Parse>();
this.consumeSemicolon();
exportDeclaration = this.finalize(node, new Node.ExportDefaultDeclaration(declaration));
}
} else if (this.match('*')) {
// export * from 'foo';
this.nextToken();
if (!this.matchContextualKeyword('from')) {
const message = this.lookahead.value ? Messages.UnexpectedToken : Messages.MissingFromClause;
this.throwError(message, this.lookahead.value);
}
this.nextToken();
const src = this.parseModuleSpecifier();
this.consumeSemicolon();
exportDeclaration = this.finalize(node, new Node.ExportAllDeclaration(src));
} else if (this.lookahead.type === Token.Keyword) {
// export var f = 1;
let declaration;
switch (this.lookahead.value) {
case 'let':
case 'const':
declaration = this.parseLexicalDeclaration({ inFor: false });
break;
case 'var':
case 'class':
case 'function':
declaration = this.statementListItem<ParseAs(StatementNode)>();
break;
default:
this.throwUnexpectedToken(this.lookahead);
}
exportDeclaration = this.finalize(node, new Node.ExportNamedDeclaration(declaration, [], null));
} else {
const specifiers = [];
let source = null;
let isExportFromIdentifier = false;
this.expect('{');
while (!this.match('}')) {
isExportFromIdentifier = isExportFromIdentifier || this.matchKeyword('default');
specifiers.push(this.parseExportSpecifier());
if (!this.match('}')) {
this.expect(',');
}
}
this.expect('}');
if (this.matchContextualKeyword('from')) {
// export {default} from 'foo';
// export {foo} from 'foo';
this.nextToken();
source = this.parseModuleSpecifier();
this.consumeSemicolon();
} else if (isExportFromIdentifier) {
// export {default}; // missing fromClause
const message = this.lookahead.value ? Messages.UnexpectedToken : Messages.MissingFromClause;
this.throwError(message, this.lookahead.value);
} else {
// export {foo};
this.consumeSemicolon();
}
exportDeclaration = this.finalize(node, new Node.ExportNamedDeclaration(null, specifiers, source));
}
return exportDeclaration;
}
*/
};
RefPtr<ProgramNode> parseProgram(::Escargot::Context* ctx, StringView source, bool strictFromOutside, size_t stackRemain)
{
Parser parser(ctx, source, stackRemain);
parser.context->strict = strictFromOutside;
RefPtr<ProgramNode> nd = parser.parseProgram();
return nd;
}
std::tuple<RefPtr<Node>, ASTScopeContext*> parseSingleFunction(::Escargot::Context* ctx, InterpretedCodeBlock* codeBlock, size_t stackRemain)
{
Parser parser(ctx, codeBlock->src(), stackRemain, codeBlock->sourceElementStart());
parser.trackUsingNames = false;
parser.config.parseSingleFunction = true;
parser.config.parseSingleFunctionTarget = codeBlock;
parser.config.reparseArguments = codeBlock->shouldReparseArguments();
auto sc = new ASTScopeContext(codeBlock->isStrict());
parser.pushScopeContext(sc);
parser.context->allowYield = !codeBlock->isGenerator();
RefPtr<Node> nd;
if (codeBlock->isArrowFunctionExpression()) {
nd = parser.parseArrowFunctionSourceElements();
} else {
nd = parser.parseFunctionSourceElements();
}
return std::make_tuple(nd, sc);
}
} // namespace esprima
} // namespace Escargot