mirror of
https://github.com/Samsung/escargot.git
synced 2026-06-22 10:01:50 +00:00
* Don't allocate new StringView on parser if possible * Store esprima::Context::firstCoverInitializedNameError as pointer. it can reduce copy cost on esprima::*CoverGrammar * Make Script as reexcutable if possible * Don't save parsed source codes - Remove SourceStringView - When create AtomicString from StringView, we should make new string data instead of reference StringView * Reduce size of StringBufferAccessData * Fix crash in toString of FunctionObject. Signed-off-by: seonghyun kim <sh8281.kim@samsung.com>
5282 lines
216 KiB
C++
5282 lines
216 KiB
C++
/*
|
|
Copyright JS Foundation and other contributors, https://js.foundation/
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
/*
|
|
* Copyright (c) 2016-present Samsung Electronics Co., Ltd
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
|
* USA
|
|
*/
|
|
|
|
#include "Escargot.h"
|
|
#include "esprima.h"
|
|
#include "Messages.h"
|
|
#include "interpreter/ByteCode.h"
|
|
#include "parser/ast/AST.h"
|
|
#include "parser/CodeBlock.h"
|
|
#include "parser/Lexer.h"
|
|
#include "parser/Script.h"
|
|
#include "parser/ASTBuilder.h"
|
|
|
|
#define ALLOC_TOKEN(tokenName) Scanner::ScannerResult* tokenName = ALLOCA(sizeof(Scanner::ScannerResult), Scanner::ScannerResult, ec);
|
|
|
|
#define ASTNode typename ASTBuilder::ASTNode
|
|
#define ASTPassNode typename ASTBuilder::ASTPassNode
|
|
#define ASTNodePtr typename ASTBuilder::ASTNodePtr
|
|
#define ASTIdentifierNode typename ASTBuilder::ASTIdentifierNode
|
|
#define ASTStatementContainer typename ASTBuilder::ASTStatementContainer
|
|
#define ASTStatementNodePtr typename ASTBuilder::ASTStatementNodePtr
|
|
#define ASTNodeVector typename ASTBuilder::ASTNodeVector
|
|
|
|
using namespace Escargot::EscargotLexer;
|
|
|
|
namespace Escargot {
|
|
namespace esprima {
|
|
|
|
struct Context {
|
|
// Escargot::esprima::Context always allocated on the stack
|
|
MAKE_STACK_ALLOCATED();
|
|
|
|
bool allowIn : 1;
|
|
bool allowYield : 1;
|
|
bool allowLexicalDeclaration : 1;
|
|
bool allowSuperCall : 1;
|
|
bool allowSuperProperty : 1;
|
|
bool isAssignmentTarget : 1;
|
|
bool isBindingElement : 1;
|
|
bool inFunctionBody : 1;
|
|
bool inIteration : 1;
|
|
bool inSwitch : 1;
|
|
bool inArrowFunction : 1;
|
|
bool inWith : 1;
|
|
bool inCatchClause : 1;
|
|
bool inLoop : 1;
|
|
bool inParameterParsing : 1;
|
|
bool hasRestrictedWordInArrayOrObjectInitializer : 1;
|
|
bool strict : 1;
|
|
std::unique_ptr<Scanner::ScannerResult> firstCoverInitializedNameError;
|
|
std::vector<AtomicString> catchClauseSimplyDeclaredVariableNames;
|
|
std::vector<std::pair<AtomicString, bool>> labelSet; // <LabelString, continue accepted>
|
|
};
|
|
|
|
struct Marker {
|
|
size_t index;
|
|
size_t lineNumber;
|
|
size_t lineStart;
|
|
};
|
|
|
|
struct MetaNode {
|
|
size_t index;
|
|
size_t line;
|
|
size_t column;
|
|
};
|
|
|
|
|
|
/*
|
|
struct ArrowParameterPlaceHolderNode {
|
|
String* type;
|
|
ExpressionNodeVector params;
|
|
};
|
|
*/
|
|
|
|
struct DeclarationOptions {
|
|
bool inFor;
|
|
KeywordKind kind;
|
|
};
|
|
|
|
class Parser {
|
|
public:
|
|
// Parser always allocated on the stack
|
|
MAKE_STACK_ALLOCATED();
|
|
|
|
::Escargot::Context* escargotContext;
|
|
Scanner* scanner;
|
|
Scanner scannerInstance;
|
|
|
|
enum SourceType {
|
|
Script,
|
|
Module
|
|
};
|
|
|
|
SourceType sourceType;
|
|
Scanner::ScannerResult lookahead;
|
|
|
|
Script::ModuleData* moduleData;
|
|
|
|
Context contextInstance;
|
|
Context* context;
|
|
|
|
CodeBlock* codeBlock;
|
|
Marker baseMarker;
|
|
Marker startMarker;
|
|
Marker lastMarker;
|
|
|
|
Vector<ASTFunctionScopeContext*, GCUtil::gc_malloc_allocator<ASTFunctionScopeContext*>> scopeContexts;
|
|
ASTFunctionScopeContext* lastPoppedScopeContext;
|
|
|
|
AtomicString lastUsingName;
|
|
std::function<void(AtomicString, LexicalBlockIndex, KeywordKind, bool)> nameDeclaredCallback;
|
|
|
|
bool trackUsingNames;
|
|
bool hasLineTerminator;
|
|
bool isParsingSingleFunction;
|
|
size_t childFunctionIndex;
|
|
size_t stackLimit;
|
|
size_t subCodeBlockIndex;
|
|
|
|
LexicalBlockIndex lexicalBlockIndex;
|
|
LexicalBlockIndex lexicalBlockCount;
|
|
|
|
ASTFunctionScopeContext fakeContext;
|
|
|
|
struct ParseFormalParametersResult {
|
|
PatternNodeVector params;
|
|
std::vector<AtomicString> paramSet;
|
|
Scanner::ScannerResult stricted;
|
|
Scanner::ScannerResult firstRestricted;
|
|
const char* message;
|
|
bool valid;
|
|
|
|
ParseFormalParametersResult()
|
|
: message(nullptr)
|
|
, valid(false)
|
|
{
|
|
}
|
|
};
|
|
|
|
Parser(::Escargot::Context* escargotContext, StringView code, bool isModule, size_t stackRemain, ExtendedNodeLOC startLoc = ExtendedNodeLOC(0, 0, 0))
|
|
: scannerInstance(escargotContext, code, startLoc.line, startLoc.column)
|
|
{
|
|
ASSERT(escargotContext != nullptr);
|
|
|
|
if (stackRemain >= STACK_LIMIT_FROM_BASE) {
|
|
stackRemain = STACK_LIMIT_FROM_BASE;
|
|
}
|
|
volatile int sp;
|
|
volatile size_t currentStackBase = (size_t)&sp;
|
|
#ifdef STACK_GROWS_DOWN
|
|
this->stackLimit = currentStackBase - stackRemain;
|
|
#else
|
|
this->stackLimit = currentStackBase + stackRemain;
|
|
#endif
|
|
this->escargotContext = escargotContext;
|
|
this->lexicalBlockIndex = 0;
|
|
this->lexicalBlockCount = 0;
|
|
this->subCodeBlockIndex = 0;
|
|
this->lastPoppedScopeContext = nullptr;
|
|
this->trackUsingNames = true;
|
|
this->isParsingSingleFunction = false;
|
|
this->codeBlock = nullptr;
|
|
this->childFunctionIndex = 0;
|
|
|
|
this->scanner = &scannerInstance;
|
|
if (stackRemain >= STACK_LIMIT_FROM_BASE) {
|
|
stackRemain = STACK_LIMIT_FROM_BASE;
|
|
}
|
|
|
|
this->sourceType = isModule ? Module : Script;
|
|
this->moduleData = isModule ? new Script::ModuleData() : nullptr;
|
|
this->hasLineTerminator = false;
|
|
|
|
this->context = &contextInstance;
|
|
this->context->allowIn = true;
|
|
this->context->allowYield = true;
|
|
this->context->allowLexicalDeclaration = false;
|
|
this->context->allowSuperCall = false;
|
|
this->context->allowSuperProperty = false;
|
|
this->context->isAssignmentTarget = true;
|
|
this->context->isBindingElement = true;
|
|
this->context->inFunctionBody = false;
|
|
this->context->inIteration = false;
|
|
this->context->inSwitch = false;
|
|
this->context->inArrowFunction = false;
|
|
this->context->inWith = false;
|
|
this->context->inCatchClause = false;
|
|
this->context->inLoop = false;
|
|
this->context->inParameterParsing = false;
|
|
this->context->hasRestrictedWordInArrayOrObjectInitializer = false;
|
|
this->context->strict = this->sourceType == Module;
|
|
this->setMarkers(startLoc);
|
|
}
|
|
|
|
void setMarkers(ExtendedNodeLOC startLoc)
|
|
{
|
|
this->baseMarker.index = startLoc.index;
|
|
this->baseMarker.lineNumber = this->scanner->lineNumber;
|
|
this->baseMarker.lineStart = 0;
|
|
|
|
this->startMarker.index = 0;
|
|
this->startMarker.lineNumber = this->scanner->lineNumber;
|
|
this->startMarker.lineStart = 0;
|
|
|
|
this->lastMarker.index = 0;
|
|
this->lastMarker.lineNumber = this->scanner->lineNumber;
|
|
this->lastMarker.lineStart = 0;
|
|
|
|
{
|
|
this->lastMarker.index = this->scanner->index;
|
|
this->lastMarker.lineNumber = this->scanner->lineNumber;
|
|
this->lastMarker.lineStart = this->scanner->lineStart;
|
|
|
|
this->collectComments();
|
|
|
|
this->startMarker.index = this->scanner->index;
|
|
this->startMarker.lineNumber = this->scanner->lineNumber;
|
|
this->startMarker.lineStart = this->scanner->lineStart;
|
|
|
|
Scanner::ScannerResult* next = &this->lookahead;
|
|
this->scanner->lex(next);
|
|
this->hasLineTerminator = false;
|
|
|
|
if (this->context->strict && next->type == Token::IdentifierToken && this->scanner->isStrictModeReservedWord(next->relatedSource())) {
|
|
next->type = Token::KeywordToken;
|
|
}
|
|
}
|
|
this->lastMarker.index = this->scanner->index;
|
|
this->lastMarker.lineNumber = this->scanner->lineNumber;
|
|
this->lastMarker.lineStart = this->scanner->lineStart;
|
|
}
|
|
|
|
ASTFunctionScopeContext* popScopeContext(const MetaNode& node)
|
|
{
|
|
auto ret = scopeContexts.back();
|
|
scopeContexts.pop_back();
|
|
this->lastUsingName = AtomicString();
|
|
this->lastPoppedScopeContext = ret;
|
|
return ret;
|
|
}
|
|
|
|
void pushScopeContext(ASTFunctionScopeContext* ctx)
|
|
{
|
|
scopeContexts.push_back(ctx);
|
|
this->lastUsingName = AtomicString();
|
|
}
|
|
|
|
ALWAYS_INLINE void insertUsingName(AtomicString name)
|
|
{
|
|
if (this->lastUsingName == name) {
|
|
return;
|
|
}
|
|
scopeContexts.back()->insertUsingName(name, this->lexicalBlockIndex);
|
|
this->lastUsingName = name;
|
|
}
|
|
|
|
void extractNamesFromFunctionParams(const ParseFormalParametersResult& paramsResult)
|
|
{
|
|
if (this->isParsingSingleFunction) {
|
|
return;
|
|
}
|
|
|
|
const PatternNodeVector& params = paramsResult.params;
|
|
const std::vector<AtomicString>& paramNames = paramsResult.paramSet;
|
|
bool hasParameterOtherThanIdentifier = false;
|
|
#ifndef NDEBUG
|
|
bool shouldHaveEqualNumberOfParameterListAndParameterName = true;
|
|
#endif
|
|
scopeContexts.back()->m_parameters.resizeWithUninitializedValues(params.size());
|
|
for (size_t i = 0; i < params.size(); i++) {
|
|
switch (params[i]->type()) {
|
|
case Identifier: {
|
|
scopeContexts.back()->m_parameters[i] = params[i]->asIdentifier()->name();
|
|
break;
|
|
}
|
|
case AssignmentPattern: {
|
|
hasParameterOtherThanIdentifier = true;
|
|
AtomicString id;
|
|
if (params[i]->asAssignmentPattern()->left()->isIdentifier()) {
|
|
id = params[i]->asAssignmentPattern()->left()->asIdentifier()->name();
|
|
}
|
|
#ifndef NDEBUG
|
|
else {
|
|
shouldHaveEqualNumberOfParameterListAndParameterName = false;
|
|
}
|
|
#endif
|
|
scopeContexts.back()->m_parameters[i] = id;
|
|
break;
|
|
}
|
|
case ArrayPattern:
|
|
case ObjectPattern: {
|
|
hasParameterOtherThanIdentifier = true;
|
|
#ifndef NDEBUG
|
|
shouldHaveEqualNumberOfParameterListAndParameterName = false;
|
|
#endif
|
|
scopeContexts.back()->m_parameters[i] = AtomicString();
|
|
break;
|
|
}
|
|
case RestElement: {
|
|
hasParameterOtherThanIdentifier = true;
|
|
#ifndef NDEBUG
|
|
shouldHaveEqualNumberOfParameterListAndParameterName = false;
|
|
#endif
|
|
scopeContexts.back()->m_parameters.erase(i);
|
|
break;
|
|
}
|
|
default: {
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
}
|
|
}
|
|
#ifndef NDEBUG
|
|
if (shouldHaveEqualNumberOfParameterListAndParameterName) {
|
|
ASSERT(params.size() == paramNames.size());
|
|
}
|
|
#endif
|
|
scopeContexts.back()->m_hasParameterOtherThanIdentifier = hasParameterOtherThanIdentifier;
|
|
for (size_t i = 0; i < paramNames.size(); i++) {
|
|
scopeContexts.back()->insertVarName(paramNames[i], 0, true);
|
|
}
|
|
|
|
// Check if any identifier names are duplicated.
|
|
if (hasParameterOtherThanIdentifier || this->context->inArrowFunction) {
|
|
if (paramNames.size() < 2) {
|
|
return;
|
|
}
|
|
for (size_t i = 0; i < paramNames.size() - 1; i++) {
|
|
// Note: using this inner for loop because std::find didn't work for some reason.
|
|
for (size_t j = i + 1; j < paramNames.size(); j++) {
|
|
if (paramNames[i] == paramNames[j]) {
|
|
this->throwError("duplicate argument names are not allowed in this context");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void pushScopeContext(AtomicString functionName)
|
|
{
|
|
this->subCodeBlockIndex++;
|
|
if (this->isParsingSingleFunction) {
|
|
fakeContext = ASTFunctionScopeContext();
|
|
pushScopeContext(&fakeContext);
|
|
return;
|
|
}
|
|
auto parentContext = scopeContexts.back();
|
|
pushScopeContext(new ASTFunctionScopeContext(this->context->strict));
|
|
scopeContexts.back()->m_functionName = functionName;
|
|
scopeContexts.back()->m_inWith = this->context->inWith;
|
|
|
|
if (parentContext) {
|
|
parentContext->m_childScopes.push_back(scopeContexts.back());
|
|
}
|
|
}
|
|
|
|
void throwError(const char* messageFormat, String* arg0 = String::emptyString, String* arg1 = String::emptyString, ErrorObject::Code code = ErrorObject::SyntaxError)
|
|
{
|
|
UTF16StringDataNonGCStd msg;
|
|
if (arg0->length() && arg1->length()) {
|
|
UTF8StringData d1 = arg0->toUTF8StringData();
|
|
UTF8StringData d2 = arg1->toUTF8StringData();
|
|
|
|
auto temp = utf8StringToUTF16String(messageFormat, strlen(messageFormat));
|
|
msg = UTF16StringDataNonGCStd(temp.data(), temp.length());
|
|
UTF16StringDataNonGCStd from(u"%s");
|
|
UTF16StringDataNonGCStd arg0Data(arg0->toUTF16StringData().data());
|
|
UTF16StringDataNonGCStd arg1Data(arg1->toUTF16StringData().data());
|
|
size_t start_pos = msg.find(from, 0);
|
|
RELEASE_ASSERT(start_pos != SIZE_MAX);
|
|
msg.replace(start_pos, from.length(), arg0Data);
|
|
|
|
start_pos = msg.find(from, start_pos + arg0Data.length());
|
|
RELEASE_ASSERT(start_pos != SIZE_MAX);
|
|
msg.replace(start_pos, from.length(), arg1Data);
|
|
} else if (arg0->length()) {
|
|
UTF8StringData d1 = arg0->toUTF8StringData();
|
|
auto temp = utf8StringToUTF16String(messageFormat, strlen(messageFormat));
|
|
msg = UTF16StringDataNonGCStd(temp.data(), temp.length());
|
|
UTF16StringDataNonGCStd from(u"%s");
|
|
UTF16StringDataNonGCStd argData(arg0->toUTF16StringData().data());
|
|
size_t start_pos = msg.find(from, 0);
|
|
RELEASE_ASSERT(start_pos != SIZE_MAX);
|
|
msg.replace(start_pos, from.length(), argData);
|
|
} else {
|
|
msg.assign(messageFormat, &messageFormat[strlen(messageFormat)]);
|
|
}
|
|
|
|
size_t index = this->lastMarker.index;
|
|
size_t line = this->lastMarker.lineNumber;
|
|
size_t column = this->lastMarker.index - this->lastMarker.lineStart + 1;
|
|
|
|
ErrorHandler::throwError(index, line, column, new UTF16String(msg.data(), msg.length()), code);
|
|
}
|
|
|
|
void replaceAll(UTF16StringDataNonGCStd& str, const UTF16StringDataNonGCStd& from, const UTF16StringDataNonGCStd& to)
|
|
{
|
|
if (from.empty())
|
|
return;
|
|
size_t start_pos = 0;
|
|
while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
|
str.replace(start_pos, from.length(), to);
|
|
start_pos += to.length();
|
|
}
|
|
}
|
|
|
|
const char* checkTokenIdentifier(const unsigned char type)
|
|
{
|
|
switch (type) {
|
|
case Token::EOFToken:
|
|
return Messages::UnexpectedEOS;
|
|
case Token::IdentifierToken:
|
|
return Messages::UnexpectedIdentifier;
|
|
case Token::NumericLiteralToken:
|
|
return Messages::UnexpectedNumber;
|
|
case Token::StringLiteralToken:
|
|
return Messages::UnexpectedString;
|
|
case Token::TemplateToken:
|
|
return Messages::UnexpectedTemplate;
|
|
default:
|
|
return Messages::UnexpectedToken;
|
|
}
|
|
}
|
|
|
|
// Throw an exception because of an unexpected token.
|
|
void throwUnexpectedToken(Scanner::ScannerResult* token, const char* message = nullptr)
|
|
{
|
|
ASSERT(token != nullptr);
|
|
const char* msg;
|
|
if (message) {
|
|
msg = message;
|
|
} else {
|
|
msg = Messages::UnexpectedToken;
|
|
}
|
|
|
|
String* value;
|
|
if (token->type != InvalidToken) {
|
|
if (!msg) {
|
|
msg = checkTokenIdentifier(token->type);
|
|
|
|
if (token->type == Token::KeywordToken) {
|
|
if (this->scanner->isFutureReservedWord(token->relatedSource())) {
|
|
msg = Messages::UnexpectedReserved;
|
|
} else if (this->context->strict && this->scanner->isStrictModeReservedWord(token->relatedSource())) {
|
|
msg = Messages::StrictReservedWord;
|
|
}
|
|
}
|
|
} else if (token->type == Token::EOFToken) {
|
|
msg = Messages::UnexpectedEOS;
|
|
}
|
|
value = (token->type == Token::TemplateToken) ? (String*)new UTF16String(std::move(token->valueTemplate->valueRaw)) : (String*)new StringView(token->relatedSource());
|
|
} else {
|
|
value = new ASCIIString("ILLEGAL");
|
|
}
|
|
|
|
// msg = msg.replace('%0', value);
|
|
UTF16StringDataNonGCStd msgData;
|
|
msgData.assign(msg, &msg[strlen(msg)]);
|
|
UTF16StringDataNonGCStd valueData = UTF16StringDataNonGCStd(value->toUTF16StringData().data());
|
|
replaceAll(msgData, UTF16StringDataNonGCStd(u"%s"), valueData);
|
|
|
|
// if (token && typeof token.lineNumber == 'number') {
|
|
if (token->type != InvalidToken) {
|
|
const size_t index = token->start;
|
|
const size_t line = token->lineNumber;
|
|
const size_t column = token->start - this->lastMarker.lineStart + 1;
|
|
ErrorHandler::throwError(index, line, column, new UTF16String(msgData.data(), msgData.length()), ErrorObject::SyntaxError);
|
|
} else {
|
|
const size_t index = this->lastMarker.index;
|
|
const size_t line = this->lastMarker.lineNumber;
|
|
const size_t column = index - this->lastMarker.lineStart + 1;
|
|
ErrorHandler::throwError(index, line, column, new UTF16String(msgData.data(), msgData.length()), ErrorObject::SyntaxError);
|
|
}
|
|
}
|
|
|
|
ALWAYS_INLINE void collectComments()
|
|
{
|
|
this->scanner->scanComments();
|
|
}
|
|
|
|
void nextToken(Scanner::ScannerResult* token = nullptr)
|
|
{
|
|
if (token) {
|
|
*token = this->lookahead;
|
|
}
|
|
size_t tokenLineNumber = this->lookahead.lineNumber;
|
|
|
|
this->lastMarker.index = this->scanner->index;
|
|
this->lastMarker.lineNumber = this->scanner->lineNumber;
|
|
this->lastMarker.lineStart = this->scanner->lineStart;
|
|
|
|
this->collectComments();
|
|
|
|
this->startMarker.index = this->scanner->index;
|
|
this->startMarker.lineNumber = this->scanner->lineNumber;
|
|
this->startMarker.lineStart = this->scanner->lineStart;
|
|
|
|
Scanner::ScannerResult* next = &this->lookahead;
|
|
this->scanner->lex(next);
|
|
this->hasLineTerminator = tokenLineNumber != next->lineNumber;
|
|
|
|
if (this->context->strict && next->type == Token::IdentifierToken && this->scanner->isStrictModeReservedWord(next->relatedSource())) {
|
|
next->type = Token::KeywordToken;
|
|
}
|
|
//this->lookahead = *next;
|
|
|
|
/*
|
|
if (this->config.tokens && next.type !== Token.EOF) {
|
|
this->tokens.push(this->convertToken(next));
|
|
}
|
|
*/
|
|
}
|
|
|
|
void nextRegexToken(Scanner::ScannerResult* token)
|
|
{
|
|
ASSERT(token != nullptr);
|
|
this->collectComments();
|
|
|
|
this->scanner->scanRegExp(token);
|
|
|
|
// Prime the next lookahead.
|
|
this->lookahead = *token;
|
|
this->nextToken();
|
|
}
|
|
|
|
MetaNode createNode()
|
|
{
|
|
MetaNode n;
|
|
n.index = this->startMarker.index + this->baseMarker.index;
|
|
n.line = this->startMarker.lineNumber;
|
|
n.column = this->startMarker.index - this->startMarker.lineStart;
|
|
return n;
|
|
}
|
|
|
|
MetaNode startNode(Scanner::ScannerResult* token)
|
|
{
|
|
ASSERT(token != nullptr);
|
|
MetaNode n;
|
|
n.index = token->start + this->baseMarker.index;
|
|
n.line = token->lineNumber;
|
|
n.column = token->start - token->lineStart;
|
|
return n;
|
|
}
|
|
|
|
template <typename T>
|
|
ALWAYS_INLINE PassRefPtr<T> finalize(MetaNode meta, T* node)
|
|
{
|
|
/*
|
|
auto type = node->type;
|
|
if (type == CallExpression) {
|
|
CallExpressionNode* c = (CallExpressionNode*)node;
|
|
if (c->callee() && c->callee()->isIdentifier() && ((IdentifierNode*)c->callee())->name() == this->escargotContext->staticStrings().eval) {
|
|
scopeContexts.back()->m_hasEval = true;
|
|
}
|
|
}
|
|
*/
|
|
|
|
node->m_loc = NodeLOC(meta.index);
|
|
return adoptRef(node);
|
|
}
|
|
|
|
ALWAYS_INLINE SyntaxNode finalize(MetaNode meta, SyntaxNode node)
|
|
{
|
|
return node;
|
|
}
|
|
|
|
// Expect the next token to match the specified punctuator.
|
|
// If not, an exception will be thrown.
|
|
void expect(PunctuatorKind value)
|
|
{
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
if (token->type != Token::PunctuatorToken || token->valuePunctuatorKind != value) {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
}
|
|
|
|
void expectCommaSeparator()
|
|
{
|
|
this->expect(PunctuatorKind::Comma);
|
|
}
|
|
|
|
// Expect the next token to match the specified keyword.
|
|
// If not, an exception will be thrown.
|
|
|
|
void expectKeyword(KeywordKind keyword)
|
|
{
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
if (token->type != Token::KeywordToken || token->valueKeywordKind != keyword) {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
}
|
|
|
|
// Return true if the next token matches the specified punctuator.
|
|
|
|
bool match(PunctuatorKind value)
|
|
{
|
|
return this->lookahead.type == Token::PunctuatorToken && this->lookahead.valuePunctuatorKind == value;
|
|
}
|
|
|
|
// Return true if the next token matches the specified keyword
|
|
|
|
bool matchKeyword(KeywordKind keyword)
|
|
{
|
|
return this->lookahead.type == Token::KeywordToken && this->lookahead.valueKeywordKind == keyword;
|
|
}
|
|
|
|
// Return true if the next token matches the specified contextual keyword
|
|
// (where an identifier is sometimes a keyword depending on the context)
|
|
|
|
bool matchContextualKeyword(const char* keyword)
|
|
{
|
|
return this->lookahead.type == Token::IdentifierToken && this->lookahead.valueStringLiteralData.equals(keyword);
|
|
}
|
|
|
|
// Return true if the next token is an assignment operator
|
|
|
|
bool matchAssign()
|
|
{
|
|
if (this->lookahead.type != Token::PunctuatorToken) {
|
|
return false;
|
|
}
|
|
PunctuatorKind op = this->lookahead.valuePunctuatorKind;
|
|
|
|
if (op >= Substitution && op < SubstitutionEnd) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void consumeSemicolon()
|
|
{
|
|
if (this->match(PunctuatorKind::SemiColon)) {
|
|
this->nextToken();
|
|
} else if (!this->hasLineTerminator) {
|
|
if (this->lookahead.type != Token::EOFToken && !this->match(PunctuatorKind::RightBrace)) {
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
this->lastMarker.index = this->startMarker.index;
|
|
this->lastMarker.lineNumber = this->startMarker.lineNumber;
|
|
this->lastMarker.lineStart = this->startMarker.lineStart;
|
|
}
|
|
}
|
|
|
|
void throwIfSuperOperationIsNotAllowed()
|
|
{
|
|
Scanner::ScannerResult token;
|
|
this->nextToken(&token);
|
|
if (this->match(LeftParenthesis)) {
|
|
if (!this->context->allowSuperCall) {
|
|
this->throwUnexpectedToken(&token);
|
|
}
|
|
} else if (this->match(Period) || this->match(LeftSquareBracket)) {
|
|
if (!this->context->allowSuperProperty) {
|
|
this->throwUnexpectedToken(&token);
|
|
}
|
|
} else {
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
}
|
|
|
|
bool qualifiedPropertyName(Scanner::ScannerResult* token)
|
|
{
|
|
switch (token->type) {
|
|
case Token::IdentifierToken:
|
|
case Token::StringLiteralToken:
|
|
case Token::BooleanLiteralToken:
|
|
case Token::NullLiteralToken:
|
|
case Token::NumericLiteralToken:
|
|
case Token::KeywordToken:
|
|
return true;
|
|
case Token::PunctuatorToken:
|
|
return token->valuePunctuatorKind == LeftSquareBracket;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool isPropertyKey(Node* key, const StringView& name, const char* value)
|
|
{
|
|
if (key->type() == Identifier) {
|
|
return ((IdentifierNode*)key)->name() == value;
|
|
} else if (key->type() == Literal) {
|
|
if (((LiteralNode*)key)->value().isString()) {
|
|
return ((LiteralNode*)key)->value().asString()->equals(value);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isPropertyKey(SyntaxNode* key, const StringView& name, const char* value)
|
|
{
|
|
if (key->type() == Identifier) {
|
|
return key->name() == value;
|
|
} else if (key->type() == Literal) {
|
|
return name.equals(value);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// Cover grammar support.
|
|
//
|
|
// When an assignment expression position starts with an left parenthesis, the determination of the type
|
|
// of the syntax is to be deferred arbitrarily long until the end of the parentheses pair (plus a lookahead)
|
|
// or the first comma. This situation also defers the determination of all the expressions nested in the pair.
|
|
//
|
|
// There are three productions that can be parsed in a parentheses pair that needs to be determined
|
|
// after the outermost pair is closed. They are:
|
|
//
|
|
// 1. AssignmentExpression
|
|
// 2. BindingElements
|
|
// 3. AssignmentTargets
|
|
//
|
|
// In order to avoid exponential backtracking, we use two flags to denote if the production can be
|
|
// binding element or assignment target.
|
|
//
|
|
// The three productions have the relationship:
|
|
//
|
|
// BindingElements ⊆ AssignmentTargets ⊆ AssignmentExpression
|
|
//
|
|
// with a single exception that CoverInitializedName when used directly in an Expression, generates
|
|
// an early error. Therefore, we need the third state, firstCoverInitializedNameError, to track the
|
|
// first usage of CoverInitializedName and report it when we reached the end of the parentheses pair.
|
|
//
|
|
// isolateCoverGrammar function runs the given parser function with a new cover grammar context, and it does not
|
|
// effect the current flags. This means the production the parser parses is only used as an expression. Therefore
|
|
// the CoverInitializedName check is conducted.
|
|
//
|
|
// inheritCoverGrammar function runs the given parse function with a new cover grammar context, and it propagates
|
|
// the flags outside of the parser. This means the production the parser parses is used as a part of a potential
|
|
// pattern. The CoverInitializedName check is deferred.
|
|
|
|
typedef Vector<Scanner::ScannerResult, GCUtil::gc_malloc_allocator<Scanner::ScannerResult>> ScannerResultVector;
|
|
|
|
struct IsolateCoverGrammarContext {
|
|
bool previousIsBindingElement;
|
|
bool previousIsAssignmentTarget;
|
|
std::unique_ptr<Scanner::ScannerResult> previousFirstCoverInitializedNameError;
|
|
};
|
|
|
|
void checkRecursiveLimit()
|
|
{
|
|
volatile int sp;
|
|
size_t currentStackBase = (size_t)&sp;
|
|
#ifdef STACK_GROWS_DOWN
|
|
if (UNLIKELY(currentStackBase < stackLimit)) {
|
|
#else
|
|
if (UNLIKELY(currentStackBase > stackLimit)) {
|
|
#endif
|
|
this->throwError("too many recursion in script", String::emptyString, String::emptyString, ErrorObject::RangeError);
|
|
}
|
|
}
|
|
|
|
void startCoverGrammar(IsolateCoverGrammarContext* grammarContext)
|
|
{
|
|
ASSERT(grammarContext != nullptr);
|
|
|
|
grammarContext->previousIsBindingElement = this->context->isBindingElement;
|
|
grammarContext->previousIsAssignmentTarget = this->context->isAssignmentTarget;
|
|
grammarContext->previousFirstCoverInitializedNameError = std::move(this->context->firstCoverInitializedNameError);
|
|
|
|
this->context->isBindingElement = true;
|
|
this->context->isAssignmentTarget = true;
|
|
|
|
checkRecursiveLimit();
|
|
}
|
|
|
|
void endIsolateCoverGrammar(IsolateCoverGrammarContext* grammarContext)
|
|
{
|
|
ASSERT(grammarContext != nullptr);
|
|
|
|
if (UNLIKELY((bool)this->context->firstCoverInitializedNameError)) {
|
|
this->throwUnexpectedToken(this->context->firstCoverInitializedNameError.get());
|
|
}
|
|
|
|
this->context->isBindingElement = grammarContext->previousIsBindingElement;
|
|
this->context->isAssignmentTarget = grammarContext->previousIsAssignmentTarget;
|
|
this->context->firstCoverInitializedNameError = std::move(grammarContext->previousFirstCoverInitializedNameError);
|
|
}
|
|
|
|
void endInheritCoverGrammar(IsolateCoverGrammarContext* grammarContext)
|
|
{
|
|
ASSERT(grammarContext != nullptr);
|
|
|
|
this->context->isBindingElement = this->context->isBindingElement && grammarContext->previousIsBindingElement;
|
|
this->context->isAssignmentTarget = this->context->isAssignmentTarget && grammarContext->previousIsAssignmentTarget;
|
|
if (UNLIKELY((bool)grammarContext->previousFirstCoverInitializedNameError)) {
|
|
this->context->firstCoverInitializedNameError = std::move(grammarContext->previousFirstCoverInitializedNameError);
|
|
}
|
|
}
|
|
|
|
template <class ASTBuilder, typename T>
|
|
ALWAYS_INLINE ASTPassNode isolateCoverGrammar(ASTBuilder& builder, T parseFunction)
|
|
{
|
|
IsolateCoverGrammarContext grammarContext;
|
|
startCoverGrammar(&grammarContext);
|
|
|
|
ASTPassNode result = (this->*parseFunction)(builder);
|
|
endIsolateCoverGrammar(&grammarContext);
|
|
|
|
return result;
|
|
}
|
|
|
|
template <class ASTBuilder, typename T>
|
|
ALWAYS_INLINE ASTPassNode isolateCoverGrammarWithFunctor(ASTBuilder& builder, T parseFunction)
|
|
{
|
|
IsolateCoverGrammarContext grammarContext;
|
|
startCoverGrammar(&grammarContext);
|
|
|
|
ASTPassNode result = parseFunction();
|
|
endIsolateCoverGrammar(&grammarContext);
|
|
|
|
return result;
|
|
}
|
|
|
|
template <class ASTBuilder, typename T>
|
|
ALWAYS_INLINE ASTPassNode inheritCoverGrammar(ASTBuilder& builder, T parseFunction)
|
|
{
|
|
IsolateCoverGrammarContext grammarContext;
|
|
startCoverGrammar(&grammarContext);
|
|
|
|
ASTPassNode result = (this->*parseFunction)(builder);
|
|
endInheritCoverGrammar(&grammarContext);
|
|
|
|
return result;
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTIdentifierNode finishIdentifier(ASTBuilder& builder, Scanner::ScannerResult* token)
|
|
{
|
|
ASSERT(token != nullptr);
|
|
ASTIdentifierNode ret;
|
|
StringView sv = token->valueStringLiteral();
|
|
const auto& a = sv.bufferAccessData();
|
|
char16_t firstCh = a.charAt(0);
|
|
if (a.length == 1 && firstCh < ESCARGOT_ASCII_TABLE_MAX) {
|
|
ret = builder.createIdentifierNode(this->escargotContext->staticStrings().asciiTable[firstCh]);
|
|
} else {
|
|
if (token->hasAllocatedString) {
|
|
ret = builder.createIdentifierNode(AtomicString(this->escargotContext, sv.string()));
|
|
} else {
|
|
ret = builder.createIdentifierNode(AtomicString(this->escargotContext, sv));
|
|
}
|
|
}
|
|
|
|
if (this->trackUsingNames) {
|
|
insertUsingName(ret->name());
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// ECMA-262 12.2 Primary Expressions
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parsePrimaryExpression(ASTBuilder& builder)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
switch (this->lookahead.type) {
|
|
case Token::IdentifierToken: {
|
|
if (this->sourceType == SourceType::Module && this->lookahead.valueKeywordKind == AwaitKeyword) {
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
return this->finalize(node, finishIdentifier(builder, token));
|
|
}
|
|
case Token::NumericLiteralToken:
|
|
case Token::StringLiteralToken:
|
|
if (this->context->strict && this->lookahead.octal) {
|
|
this->throwUnexpectedToken(&this->lookahead, Messages::StrictOctalLiteral);
|
|
}
|
|
if (this->context->strict && this->lookahead.startWithZero) {
|
|
this->throwUnexpectedToken(&this->lookahead, Messages::StrictLeadingZeroLiteral);
|
|
}
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
{
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
// raw = this->getTokenRaw(token);
|
|
if (token->type == Token::NumericLiteralToken) {
|
|
if (this->context->inLoop || token->valueNumber == 0)
|
|
this->scopeContexts.back()->insertNumeralLiteral(Value(token->valueNumber));
|
|
return this->finalize(node, builder.createLiteralNode(Value(token->valueNumber)));
|
|
} else {
|
|
return this->finalize(node, builder.createLiteralNode(token->valueStringLiteralForAST()));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case Token::BooleanLiteralToken: {
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
// token.value = (token.value === 'true');
|
|
// raw = this->getTokenRaw(token);
|
|
{
|
|
bool value = token->relatedSource() == "true";
|
|
this->scopeContexts.back()->insertNumeralLiteral(Value(value));
|
|
return this->finalize(node, builder.createLiteralNode(Value(value)));
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Token::NullLiteralToken: {
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
// token.value = null;
|
|
// raw = this->getTokenRaw(token);
|
|
this->scopeContexts.back()->insertNumeralLiteral(Value(Value::Null));
|
|
return this->finalize(node, builder.createLiteralNode(Value(Value::Null)));
|
|
}
|
|
case Token::TemplateToken:
|
|
return this->parseTemplateLiteral(builder);
|
|
|
|
case Token::PunctuatorToken: {
|
|
PunctuatorKind value = this->lookahead.valuePunctuatorKind;
|
|
switch (value) {
|
|
case LeftParenthesis:
|
|
this->context->isBindingElement = false;
|
|
return this->inheritCoverGrammar(builder, &Parser::parseGroupExpression<ASTBuilder>);
|
|
case LeftSquareBracket:
|
|
return this->inheritCoverGrammar(builder, &Parser::parseArrayInitializer<ASTBuilder>);
|
|
case LeftBrace:
|
|
return this->inheritCoverGrammar(builder, &Parser::parseObjectInitializer<ASTBuilder>);
|
|
case Divide:
|
|
case DivideEqual: {
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
this->scanner->index = this->startMarker.index;
|
|
ALLOC_TOKEN(token);
|
|
this->nextRegexToken(token);
|
|
// raw = this->getTokenRaw(token);
|
|
return this->finalize(node, builder.createRegExpLiteralNode(token->valueRegexp.body, token->valueRegexp.flags));
|
|
}
|
|
default: {
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Token::KeywordToken:
|
|
if (!this->context->strict && this->context->allowYield && this->matchKeyword(YieldKeyword)) {
|
|
return this->parseIdentifierName(builder);
|
|
} else if (!this->context->strict && this->matchKeyword(LetKeyword)) {
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
throwUnexpectedToken(token);
|
|
} else {
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
if (this->matchKeyword(FunctionKeyword)) {
|
|
return this->parseFunctionExpression(builder);
|
|
} else if (this->matchKeyword(ThisKeyword)) {
|
|
this->nextToken();
|
|
return this->finalize(node, builder.createThisExpressionNode());
|
|
} else if (this->matchKeyword(SuperKeyword)) {
|
|
throwIfSuperOperationIsNotAllowed();
|
|
return this->finalize(node, builder.createSuperExpressionNode(this->lookahead.valuePunctuatorKind == LeftParenthesis));
|
|
} else if (this->matchKeyword(ClassKeyword)) {
|
|
return this->parseClassExpression(builder);
|
|
} else {
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
}
|
|
break;
|
|
default: {
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
}
|
|
|
|
ASSERT_NOT_REACHED();
|
|
return ASTPassNode();
|
|
}
|
|
|
|
void validateParam(ParseFormalParametersResult& options, const Scanner::ScannerResult* param, AtomicString name)
|
|
{
|
|
ASSERT(param != nullptr);
|
|
if (this->context->strict) {
|
|
if (this->scanner->isRestrictedWord(name)) {
|
|
options.stricted = *param;
|
|
options.message = Messages::StrictParamName;
|
|
}
|
|
if (std::find(options.paramSet.begin(), options.paramSet.end(), name) != options.paramSet.end()) {
|
|
options.stricted = *param;
|
|
options.message = Messages::StrictParamDupe;
|
|
}
|
|
} else if (!options.firstRestricted) {
|
|
if (this->scanner->isRestrictedWord(name)) {
|
|
options.firstRestricted = *param;
|
|
options.message = Messages::StrictParamName;
|
|
} else if (this->scanner->isStrictModeReservedWord(name)) {
|
|
options.firstRestricted = *param;
|
|
options.message = Messages::StrictReservedWord;
|
|
} else if (std::find(options.paramSet.begin(), options.paramSet.end(), name) != options.paramSet.end()) {
|
|
options.stricted = *param;
|
|
options.message = Messages::StrictParamDupe;
|
|
}
|
|
}
|
|
|
|
options.paramSet.push_back(name);
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseRestElement(ASTBuilder& builder, ScannerResultVector& params)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
this->expect(PeriodPeriodPeriod);
|
|
ASTNode arg = this->parsePattern(builder, params);
|
|
if (this->match(Substitution)) {
|
|
this->throwError(Messages::DefaultRestParameter);
|
|
}
|
|
if (!this->match(RightParenthesis)) {
|
|
this->throwError(Messages::ParameterAfterRestParameter);
|
|
}
|
|
return this->finalize(node, builder.createRestElementNode(arg.get()));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseBindingRestElement(ASTBuilder& builder, ScannerResultVector& params, KeywordKind kind = KeywordKindEnd, bool isExplicitVariableDeclaration = false)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expect(PeriodPeriodPeriod);
|
|
ASTNode arg = this->parsePattern(builder, params, kind, isExplicitVariableDeclaration);
|
|
return this->finalize(node, builder.createRestElementNode(arg.get()));
|
|
}
|
|
|
|
// ECMA-262 13.3.3 Destructuring Binding Patterns
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseArrayPattern(ASTBuilder& builder, ScannerResultVector& params, KeywordKind kind = KeywordKindEnd, bool isExplicitVariableDeclaration = false)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
this->expect(LeftSquareBracket);
|
|
ASTNodeVector elements;
|
|
while (!this->match(RightSquareBracket)) {
|
|
if (this->match(Comma)) {
|
|
this->nextToken();
|
|
elements.push_back(nullptr);
|
|
} else {
|
|
if (this->match(PeriodPeriodPeriod)) {
|
|
elements.push_back(this->parseBindingRestElement(builder, params, kind, isExplicitVariableDeclaration));
|
|
break;
|
|
} else {
|
|
elements.push_back(this->parsePatternWithDefault(builder, params, kind, isExplicitVariableDeclaration));
|
|
}
|
|
if (!this->match(RightSquareBracket)) {
|
|
this->expect(Comma);
|
|
}
|
|
}
|
|
}
|
|
this->expect(RightSquareBracket);
|
|
|
|
return this->finalize(node, builder.createArrayPatternNode(std::move(elements)));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parsePropertyPattern(ASTBuilder& builder, ScannerResultVector& params, KeywordKind kind = KeywordKindEnd, bool isExplicitVariableDeclaration = false)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
bool computed = false;
|
|
bool shorthand = false;
|
|
bool method = false;
|
|
|
|
ASTNode keyNode; //'': Node.PropertyKey;
|
|
ASTNode valueNode; //: Node.PropertyValue;
|
|
|
|
if (this->lookahead.type == Token::IdentifierToken) {
|
|
ALLOC_TOKEN(keyToken);
|
|
*keyToken = this->lookahead;
|
|
keyNode = this->parseVariableIdentifier(builder);
|
|
|
|
if (this->match(Substitution)) {
|
|
params.push_back(*keyToken);
|
|
shorthand = true;
|
|
this->nextToken();
|
|
|
|
ASTNode expr = this->parseAssignmentExpression<ASTBuilder, false>(builder);
|
|
valueNode = this->finalize(this->startNode(keyToken), builder.createAssignmentPatternNode(keyNode.get(), expr.get()));
|
|
} else if (!this->match(Colon)) {
|
|
params.push_back(*keyToken);
|
|
if (isExplicitVariableDeclaration) {
|
|
ASSERT(kind == KeywordKind::VarKeyword || kind == KeywordKind::LetKeyword || kind == KeywordKind::ConstKeyword);
|
|
addDeclaredNameIntoContext(keyNode->asIdentifier()->name(), this->lexicalBlockIndex, kind, isExplicitVariableDeclaration);
|
|
}
|
|
shorthand = true;
|
|
valueNode = keyNode;
|
|
} else {
|
|
this->expect(Colon);
|
|
valueNode = this->parsePatternWithDefault(builder, params, kind, isExplicitVariableDeclaration);
|
|
}
|
|
} else {
|
|
computed = this->match(LeftSquareBracket);
|
|
StringView keyString;
|
|
keyNode = this->parseObjectPropertyKey(builder, keyString);
|
|
this->expect(Colon);
|
|
valueNode = this->parsePatternWithDefault(builder, params, kind, isExplicitVariableDeclaration);
|
|
}
|
|
|
|
return this->finalize(node, builder.createPropertyNode(keyNode.get(), valueNode.get(), PropertyNode::Kind::Init, computed, shorthand));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseObjectPattern(ASTBuilder& builder, ScannerResultVector& params, KeywordKind kind = KeywordKindEnd, bool isExplicitVariableDeclaration = false)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
ASTNodeVector properties;
|
|
|
|
this->expect(LeftBrace);
|
|
while (!this->match(RightBrace)) {
|
|
properties.push_back(this->parsePropertyPattern(builder, params, kind, isExplicitVariableDeclaration));
|
|
|
|
if (!this->match(RightBrace)) {
|
|
this->expect(Comma);
|
|
}
|
|
}
|
|
this->expect(RightBrace);
|
|
|
|
return this->finalize(node, builder.createObjectPatternNode(std::move(properties)));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parsePattern(ASTBuilder& builder, ScannerResultVector& params, KeywordKind kind = KeywordKindEnd, bool isExplicitVariableDeclaration = false)
|
|
{
|
|
if (this->match(LeftSquareBracket)) {
|
|
return this->parseArrayPattern(builder, params, kind, isExplicitVariableDeclaration);
|
|
} else if (this->match(LeftBrace)) {
|
|
return this->parseObjectPattern(builder, params, kind, isExplicitVariableDeclaration);
|
|
} else {
|
|
if (this->matchKeyword(LetKeyword) && (kind == ConstKeyword || kind == LetKeyword)) {
|
|
this->throwUnexpectedToken(&this->lookahead, Messages::UnexpectedToken);
|
|
}
|
|
params.push_back(this->lookahead);
|
|
return this->parseVariableIdentifier(builder, kind, isExplicitVariableDeclaration);
|
|
}
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parsePatternWithDefault(ASTBuilder& builder, ScannerResultVector& params, KeywordKind kind = KeywordKindEnd, bool isExplicitVariableDeclaration = false)
|
|
{
|
|
ALLOC_TOKEN(startToken);
|
|
*startToken = this->lookahead;
|
|
|
|
ASTPassNode pattern = this->parsePattern(builder, params, kind, isExplicitVariableDeclaration);
|
|
if (this->match(PunctuatorKind::Substitution)) {
|
|
this->nextToken();
|
|
const bool previousAllowYield = this->context->allowYield;
|
|
ASTPassNode right = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
|
|
|
|
return this->finalize(this->startNode(startToken), builder.createAssignmentPatternNode(pattern.get(), right.get()));
|
|
}
|
|
|
|
return pattern;
|
|
}
|
|
|
|
bool parseFormalParameter(ParseFormalParametersResult& options)
|
|
{
|
|
RefPtr<Node> param;
|
|
bool trackUsingNamesBefore = this->trackUsingNames;
|
|
this->trackUsingNames = false;
|
|
ScannerResultVector params;
|
|
ALLOC_TOKEN(token);
|
|
*token = this->lookahead;
|
|
|
|
// FIXME allocate temporal NodeGenerator because current parameter parsing is done by NodeGenerator
|
|
NodeGenerator builder;
|
|
if (token->type == Token::PunctuatorToken && token->valuePunctuatorKind == PunctuatorKind::PeriodPeriodPeriod) {
|
|
param = this->parseRestElement(builder, params);
|
|
} else {
|
|
param = this->parsePatternWithDefault(builder, params);
|
|
}
|
|
for (size_t i = 0; i < params.size(); i++) {
|
|
AtomicString as(this->escargotContext, params[i].relatedSource());
|
|
this->validateParam(options, ¶ms[i], as);
|
|
}
|
|
options.params.push_back(param);
|
|
this->trackUsingNames = trackUsingNamesBefore;
|
|
return !this->match(PunctuatorKind::RightParenthesis);
|
|
}
|
|
|
|
ParseFormalParametersResult parseFormalParameters(Scanner::ScannerResult* firstRestricted = nullptr)
|
|
{
|
|
this->context->inParameterParsing = true;
|
|
ParseFormalParametersResult options;
|
|
|
|
if (firstRestricted) {
|
|
options.firstRestricted = *firstRestricted;
|
|
}
|
|
|
|
size_t oldSubCodeBlockIndex = this->subCodeBlockIndex;
|
|
|
|
if (!this->match(RightParenthesis)) {
|
|
options.paramSet.clear();
|
|
while (this->startMarker.index < this->scanner->length) {
|
|
if (!this->parseFormalParameter(options)) {
|
|
break;
|
|
}
|
|
this->expect(Comma);
|
|
}
|
|
}
|
|
this->expect(RightParenthesis);
|
|
|
|
this->subCodeBlockIndex = oldSubCodeBlockIndex;
|
|
|
|
this->context->inParameterParsing = false;
|
|
options.valid = true;
|
|
return options;
|
|
}
|
|
|
|
// ECMA-262 12.2.5 Array Initializer
|
|
|
|
template <class ASTBuilder, bool checkLeftHasRestrictedWord>
|
|
ASTPassNode parseSpreadElement(ASTBuilder& builder)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expect(PunctuatorKind::PeriodPeriodPeriod);
|
|
|
|
ASTNode arg;
|
|
arg = this->inheritCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, checkLeftHasRestrictedWord>);
|
|
if (arg->isAssignmentOperation()) {
|
|
this->throwError(Messages::DefaultRestParameter);
|
|
}
|
|
|
|
return this->finalize(node, builder.createSpreadElementNode(arg.get()));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseArrayInitializer(ASTBuilder& builder)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
ASTNodeVector elements;
|
|
|
|
this->expect(LeftSquareBracket);
|
|
|
|
bool hasSpreadElement = false;
|
|
|
|
while (!this->match(RightSquareBracket)) {
|
|
if (this->match(Comma)) {
|
|
this->nextToken();
|
|
elements.push_back(nullptr);
|
|
} else if (this->match(PeriodPeriodPeriod)) {
|
|
elements.push_back(this->parseSpreadElement<ASTBuilder, true>(builder));
|
|
hasSpreadElement = true;
|
|
if (!this->match(RightSquareBracket)) {
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
this->expect(Comma);
|
|
}
|
|
} else {
|
|
elements.push_back(this->inheritCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, true>));
|
|
if (!this->match(RightSquareBracket)) {
|
|
this->expect(Comma);
|
|
}
|
|
}
|
|
}
|
|
this->expect(RightSquareBracket);
|
|
|
|
return this->finalize(node, builder.createArrayExpressionNode(std::move(elements), AtomicString(), nullptr, hasSpreadElement, false));
|
|
}
|
|
|
|
// ECMA-262 12.2.6 Object Initializer
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parsePropertyMethod(ASTBuilder& builder, ParseFormalParametersResult& params)
|
|
{
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
|
|
const bool previousStrict = this->context->strict;
|
|
ASTPassNode body = this->isolateCoverGrammar(builder, &Parser::parseFunctionSourceElements<ASTBuilder>);
|
|
if (this->context->strict && params.firstRestricted) {
|
|
this->throwUnexpectedToken(¶ms.firstRestricted, params.message);
|
|
}
|
|
if (this->context->strict && params.stricted) {
|
|
this->throwUnexpectedToken(¶ms.stricted, params.message);
|
|
}
|
|
this->context->strict = previousStrict;
|
|
|
|
return body;
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parsePropertyMethodFunction(ASTBuilder& builder, bool allowSuperCall)
|
|
{
|
|
const bool isGenerator = false;
|
|
const bool previousAllowYield = this->context->allowYield;
|
|
const bool previousAllowSuperCall = this->context->allowSuperCall;
|
|
const bool previousAllowSuperProperty = this->context->allowSuperProperty;
|
|
const bool previousInArrowFunction = this->context->inArrowFunction;
|
|
|
|
this->context->allowYield = true;
|
|
this->context->inArrowFunction = false;
|
|
this->context->allowSuperProperty = true;
|
|
|
|
if (allowSuperCall) {
|
|
this->context->allowSuperCall = true;
|
|
}
|
|
|
|
MetaNode node = this->createNode();
|
|
this->expect(LeftParenthesis);
|
|
pushScopeContext(AtomicString());
|
|
ParseFormalParametersResult params = this->parseFormalParameters();
|
|
extractNamesFromFunctionParams(params);
|
|
ASTNode method = this->parsePropertyMethod(builder, params);
|
|
|
|
this->context->allowYield = previousAllowYield;
|
|
this->context->inArrowFunction = previousInArrowFunction;
|
|
this->context->allowSuperProperty = previousAllowSuperProperty;
|
|
this->context->allowSuperCall = previousAllowSuperCall;
|
|
|
|
scopeContexts.back()->m_paramsStartLOC.index = node.index;
|
|
scopeContexts.back()->m_paramsStartLOC.column = node.column;
|
|
scopeContexts.back()->m_paramsStartLOC.line = node.line;
|
|
scopeContexts.back()->m_allowSuperProperty = true;
|
|
if (allowSuperCall) {
|
|
scopeContexts.back()->m_allowSuperCall = true;
|
|
}
|
|
scopeContexts.back()->m_nodeType = ASTNodeType::FunctionExpression;
|
|
scopeContexts.back()->m_isGenerator = isGenerator;
|
|
return this->finalize(node, builder.createFunctionExpressionNode(popScopeContext(node), isGenerator, this->subCodeBlockIndex));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseObjectPropertyKey(ASTBuilder& builder, StringView& keyString)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
|
|
ASTNode key;
|
|
switch (token->type) {
|
|
case Token::NumericLiteralToken:
|
|
case Token::StringLiteralToken:
|
|
if (this->context->strict && token->octal) {
|
|
this->throwUnexpectedToken(token, Messages::StrictOctalLiteral);
|
|
}
|
|
if (this->context->strict && this->lookahead.startWithZero) {
|
|
this->throwUnexpectedToken(&this->lookahead, Messages::StrictLeadingZeroLiteral);
|
|
}
|
|
// const raw = this->getTokenRaw(token);
|
|
{
|
|
Value v;
|
|
if (token->type == Token::NumericLiteralToken) {
|
|
if (this->context->inLoop || token->valueNumber == 0)
|
|
this->scopeContexts.back()->insertNumeralLiteral(Value(token->valueNumber));
|
|
v = Value(token->valueNumber);
|
|
} else {
|
|
keyString = token->valueStringLiteral();
|
|
v = Value(token->valueStringLiteralForAST());
|
|
}
|
|
key = this->finalize(node, builder.createLiteralNode(v));
|
|
}
|
|
break;
|
|
|
|
case Token::IdentifierToken:
|
|
case Token::BooleanLiteralToken:
|
|
case Token::NullLiteralToken:
|
|
case Token::KeywordToken: {
|
|
bool trackUsingNamesBefore = this->trackUsingNames;
|
|
this->trackUsingNames = false;
|
|
key = this->finalize(node, finishIdentifier(builder, token));
|
|
keyString = StringView(key->asIdentifier()->name().string());
|
|
this->trackUsingNames = trackUsingNamesBefore;
|
|
break;
|
|
}
|
|
case Token::PunctuatorToken:
|
|
if (token->valuePunctuatorKind == LeftSquareBracket) {
|
|
key = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
|
|
this->expect(RightSquareBracket);
|
|
} else {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
|
|
return key;
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseObjectProperty(ASTBuilder& builder, bool& hasProto) //: Node.Property
|
|
{
|
|
ALLOC_TOKEN(token);
|
|
*token = this->lookahead;
|
|
MetaNode node = this->createNode();
|
|
|
|
PropertyNode::Kind kind;
|
|
ASTNode keyNode; //'': Node.PropertyKey;
|
|
ASTNode valueNode; //: Node.PropertyValue;
|
|
StringView keyString;
|
|
|
|
bool computed = false;
|
|
bool method = false;
|
|
bool shorthand = false;
|
|
bool isProto = false;
|
|
|
|
if (token->type == Token::IdentifierToken) {
|
|
this->nextToken();
|
|
keyNode = this->finalize(node, finishIdentifier(builder, token));
|
|
} else if (this->match(PunctuatorKind::Multiply)) {
|
|
this->nextToken();
|
|
} else {
|
|
computed = this->match(LeftSquareBracket);
|
|
keyNode = this->parseObjectPropertyKey(builder, keyString);
|
|
}
|
|
|
|
bool lookaheadPropertyKey = this->qualifiedPropertyName(&this->lookahead);
|
|
bool isGet = false;
|
|
bool isSet = false;
|
|
bool needImplictName = false;
|
|
if (token->type == Token::IdentifierToken && lookaheadPropertyKey) {
|
|
StringView sv = token->valueStringLiteral();
|
|
const auto& d = sv.bufferAccessData();
|
|
if (d.length == 3) {
|
|
if (d.equalsSameLength("get")) {
|
|
isGet = true;
|
|
} else if (d.equalsSameLength("set")) {
|
|
isSet = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isGet) {
|
|
kind = PropertyNode::Kind::Get;
|
|
computed = this->match(LeftSquareBracket);
|
|
keyNode = this->parseObjectPropertyKey(builder, keyString);
|
|
this->context->allowYield = false;
|
|
valueNode = this->parseGetterMethod(builder);
|
|
} else if (isSet) {
|
|
kind = PropertyNode::Kind::Set;
|
|
computed = this->match(LeftSquareBracket);
|
|
keyNode = this->parseObjectPropertyKey(builder, keyString);
|
|
valueNode = this->parseSetterMethod(builder);
|
|
} else if (token->type == Token::PunctuatorToken && token->valuePunctuatorKind == PunctuatorKind::Multiply && lookaheadPropertyKey) {
|
|
kind = PropertyNode::Kind::Init;
|
|
computed = this->match(LeftSquareBracket);
|
|
keyNode = this->parseObjectPropertyKey(builder, keyString);
|
|
valueNode = this->parseGeneratorMethod(builder);
|
|
method = true;
|
|
} else {
|
|
if (!keyNode) {
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
kind = PropertyNode::Kind::Init;
|
|
if (this->match(PunctuatorKind::Colon)) {
|
|
// FIXME check !this->isParsingSingleFunction
|
|
isProto = !this->isParsingSingleFunction && this->isPropertyKey(keyNode.get(), keyString, "__proto__");
|
|
|
|
if (!computed && isProto) {
|
|
if (hasProto) {
|
|
this->throwError(Messages::DuplicateProtoProperty);
|
|
}
|
|
hasProto = true;
|
|
}
|
|
this->nextToken();
|
|
ASTNodeType type = ASTNodeType::ASTNodeTypeError;
|
|
valueNode = this->inheritCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, true>);
|
|
if (valueNode) {
|
|
type = valueNode->type();
|
|
}
|
|
|
|
if ((type == ASTNodeType::FunctionExpression || type == ASTNodeType::ArrowFunctionExpression) && this->lastPoppedScopeContext->m_functionName == AtomicString()) {
|
|
needImplictName = true;
|
|
}
|
|
} else if (this->match(LeftParenthesis)) {
|
|
valueNode = this->parsePropertyMethodFunction(builder, false);
|
|
method = true;
|
|
} else {
|
|
if (token->type != Token::IdentifierToken) {
|
|
if (token->type == Token::KeywordToken && token->valueKeywordKind == KeywordKind::YieldKeyword) {
|
|
// yield is a valid Identifier in AssignmentProperty outside of strict mode and generator functions
|
|
if (!this->context->allowYield) {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
} else if (token->type == Token::KeywordToken && token->valueKeywordKind == KeywordKind::LetKeyword) {
|
|
// In non-strict mode, let is a valid Identifier
|
|
if (this->context->strict) {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
} else {
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
}
|
|
|
|
if (this->match(Substitution)) {
|
|
this->context->firstCoverInitializedNameError.reset(new Scanner::ScannerResult(this->lookahead));
|
|
this->nextToken();
|
|
shorthand = true;
|
|
ASTNode init = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
|
|
valueNode = this->finalize(node, builder.createAssignmentPatternNode(keyNode.get(), init.get()));
|
|
} else {
|
|
shorthand = true;
|
|
valueNode = keyNode;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check if restricted words are used as target in array/object initializer
|
|
if (shorthand && this->context->strict && token->type == Token::IdentifierToken) {
|
|
AtomicString name;
|
|
name = keyNode->asIdentifier()->name();
|
|
if (this->scanner->isRestrictedWord(name)) {
|
|
this->context->hasRestrictedWordInArrayOrObjectInitializer = true;
|
|
}
|
|
}
|
|
|
|
if (!this->isParsingSingleFunction && (method || isGet || isSet || needImplictName)) {
|
|
AtomicString as;
|
|
if (!computed && keyNode->isIdentifier()) {
|
|
as = keyNode->asIdentifier()->name();
|
|
}
|
|
this->lastPoppedScopeContext->m_functionName = as;
|
|
if (needImplictName) {
|
|
this->lastPoppedScopeContext->m_hasImplictFunctionName = true;
|
|
} else {
|
|
this->lastPoppedScopeContext->m_isClassMethod = true;
|
|
}
|
|
}
|
|
return this->finalize(node, builder.createPropertyNode(keyNode.get(), valueNode.get(), kind, computed, shorthand));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseObjectInitializer(ASTBuilder& builder)
|
|
{
|
|
this->expect(LeftBrace);
|
|
MetaNode node = this->createNode();
|
|
ASTNodeVector properties;
|
|
|
|
bool hasProto = false;
|
|
while (!this->match(RightBrace)) {
|
|
properties.push_back(this->parseObjectProperty(builder, hasProto));
|
|
if (!this->match(RightBrace)) {
|
|
this->expectCommaSeparator();
|
|
}
|
|
}
|
|
this->expect(RightBrace);
|
|
|
|
return this->finalize(node, builder.createObjectExpressionNode(std::move(properties)));
|
|
}
|
|
|
|
// ECMA-262 12.2.9 Template Literals
|
|
// FIXME
|
|
TemplateElement* parseTemplateHead()
|
|
{
|
|
ASSERT(this->lookahead.type == Token::TemplateToken);
|
|
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
MetaNode node = this->createNode();
|
|
TemplateElement* tm = new TemplateElement();
|
|
tm->value = token->valueTemplate->valueCooked;
|
|
tm->valueRaw = token->valueTemplate->valueRaw;
|
|
tm->tail = token->valueTemplate->tail;
|
|
return tm;
|
|
}
|
|
|
|
// FIXME
|
|
TemplateElement* parseTemplateElement()
|
|
{
|
|
if (!this->match(PunctuatorKind::RightBrace)) {
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
|
|
// Re-scan the current token (right brace) as a template string.
|
|
this->scanner->scanTemplate(&this->lookahead);
|
|
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
MetaNode node = this->createNode();
|
|
TemplateElement* tm = new TemplateElement();
|
|
tm->value = token->valueTemplate->valueCooked;
|
|
tm->valueRaw = token->valueTemplate->valueRaw;
|
|
tm->tail = token->valueTemplate->tail;
|
|
return tm;
|
|
}
|
|
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseTemplateLiteral(ASTBuilder& builder)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
ASTNodeVector expressions;
|
|
TemplateElementVector* quasis = new (GC) TemplateElementVector;
|
|
quasis->push_back(this->parseTemplateHead());
|
|
while (!quasis->back()->tail) {
|
|
expressions.push_back(this->parseExpression(builder));
|
|
TemplateElement* quasi = this->parseTemplateElement();
|
|
quasis->push_back(quasi);
|
|
}
|
|
return this->finalize(node, builder.createTemplateLiteralNode(quasis, std::move(expressions)));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseGroupExpression(ASTBuilder& builder)
|
|
{
|
|
ASTNode exprNode;
|
|
|
|
this->expect(LeftParenthesis);
|
|
if (this->match(RightParenthesis)) {
|
|
this->nextToken();
|
|
if (!this->match(Arrow)) {
|
|
this->expect(Arrow);
|
|
}
|
|
|
|
// FIXME finalize really necessary for ArrowParameterHolderNode?
|
|
exprNode = this->finalize(this->createNode(), builder.createArrowParameterPlaceHolderNode());
|
|
} else {
|
|
ALLOC_TOKEN(startToken);
|
|
*startToken = this->lookahead;
|
|
ScannerResultVector params;
|
|
if (this->match(PeriodPeriodPeriod)) {
|
|
exprNode = this->parseRestElement(builder, params);
|
|
this->expect(RightParenthesis);
|
|
if (!this->match(Arrow)) {
|
|
this->expect(Arrow);
|
|
}
|
|
exprNode = this->finalize(this->createNode(), builder.createArrowParameterPlaceHolderNode(exprNode.get()));
|
|
} else {
|
|
bool arrow = false;
|
|
this->context->isBindingElement = true;
|
|
exprNode = this->inheritCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
|
|
|
|
if (this->match(Comma)) {
|
|
ASTNodeVector expressions;
|
|
|
|
this->context->isAssignmentTarget = false;
|
|
expressions.push_back(exprNode);
|
|
while (this->startMarker.index < this->scanner->length) {
|
|
if (!this->match(Comma)) {
|
|
break;
|
|
}
|
|
this->nextToken();
|
|
|
|
if (this->match(RightParenthesis)) {
|
|
this->nextToken();
|
|
for (size_t i = 0; i < expressions.size(); i++) {
|
|
expressions[i] = builder.reinterpretExpressionAsPattern(expressions[i].get());
|
|
}
|
|
arrow = true;
|
|
exprNode = this->finalize(this->createNode(), builder.createArrowParameterPlaceHolderNode(std::move(expressions)));
|
|
} else if (this->match(PeriodPeriodPeriod)) {
|
|
if (!this->context->isBindingElement) {
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
expressions.push_back(this->parseRestElement(builder, params));
|
|
this->expect(RightParenthesis);
|
|
if (!this->match(Arrow)) {
|
|
this->expect(Arrow);
|
|
}
|
|
this->context->isBindingElement = false;
|
|
for (size_t i = 0; i < expressions.size(); i++) {
|
|
expressions[i] = builder.reinterpretExpressionAsPattern(expressions[i].get());
|
|
}
|
|
arrow = true;
|
|
exprNode = this->finalize(this->createNode(), builder.createArrowParameterPlaceHolderNode(std::move(expressions)));
|
|
} else {
|
|
expressions.push_back(this->inheritCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>));
|
|
}
|
|
if (arrow) {
|
|
break;
|
|
}
|
|
}
|
|
if (!arrow) {
|
|
exprNode = this->finalize(this->startNode(startToken), builder.createSequenceExpressionNode(std::move(expressions)));
|
|
}
|
|
}
|
|
|
|
if (!arrow) {
|
|
this->expect(RightParenthesis);
|
|
if (this->match(Arrow)) {
|
|
if (exprNode->type() == Identifier && exprNode->asIdentifier()->name() == "yield") {
|
|
arrow = true;
|
|
exprNode = this->finalize(this->createNode(), builder.createArrowParameterPlaceHolderNode(exprNode.get()));
|
|
}
|
|
if (!arrow) {
|
|
if (!this->context->isBindingElement) {
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
|
|
if (builder.isNodeGenerator() && (exprNode->type() == SequenceExpression)) {
|
|
ASTNodeVector& expressions = exprNode->asSequenceExpression()->expressions();
|
|
for (size_t i = 0; i < expressions.size(); i++) {
|
|
expressions[i] = builder.reinterpretExpressionAsPattern(expressions[i].get());
|
|
}
|
|
} else {
|
|
exprNode = builder.reinterpretExpressionAsPattern(exprNode.get());
|
|
}
|
|
|
|
ASTNodeVector params;
|
|
if (builder.isNodeGenerator() && (exprNode->type() == SequenceExpression)) {
|
|
params = exprNode->asSequenceExpression()->expressions();
|
|
} else {
|
|
params.push_back(exprNode);
|
|
}
|
|
|
|
exprNode = this->finalize(this->createNode(), builder.createArrowParameterPlaceHolderNode(std::move(params)));
|
|
}
|
|
}
|
|
this->context->isBindingElement = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return exprNode.release();
|
|
}
|
|
|
|
// ECMA-262 12.3 Left-Hand-Side Expressions
|
|
|
|
template <class ASTBuilder>
|
|
ASTNodeVector parseArguments(ASTBuilder& builder)
|
|
{
|
|
this->expect(LeftParenthesis);
|
|
ASTNodeVector args;
|
|
if (!this->match(RightParenthesis)) {
|
|
while (true) {
|
|
ASTNode expr;
|
|
if (this->match(PeriodPeriodPeriod)) {
|
|
expr = this->parseSpreadElement<ASTBuilder, false>(builder);
|
|
} else {
|
|
expr = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
|
|
}
|
|
args.push_back(expr);
|
|
if (this->match(RightParenthesis)) {
|
|
break;
|
|
}
|
|
this->expectCommaSeparator();
|
|
}
|
|
}
|
|
this->expect(RightParenthesis);
|
|
if (UNLIKELY(args.size() > 65535)) {
|
|
this->throwError("too many arguments in call");
|
|
}
|
|
|
|
return args;
|
|
}
|
|
|
|
bool isIdentifierName(Scanner::ScannerResult* token)
|
|
{
|
|
ASSERT(token != nullptr);
|
|
return token->type == Token::IdentifierToken || token->type == Token::KeywordToken || token->type == Token::BooleanLiteralToken || token->type == Token::NullLiteralToken;
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseIdentifierName(ASTBuilder& builder)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
if (!this->isIdentifierName(token)) {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
return this->finalize(node, finishIdentifier(builder, token));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseNewExpression(ASTBuilder& builder)
|
|
{
|
|
this->nextToken();
|
|
|
|
if (this->match(Period)) {
|
|
this->nextToken();
|
|
if (this->lookahead.type == Token::IdentifierToken && this->context->inFunctionBody && this->lookahead.relatedSource() == "target") {
|
|
this->nextToken();
|
|
scopeContexts.back()->m_hasSuperOrNewTarget = true;
|
|
MetaNode node = this->createNode();
|
|
return this->finalize(node, builder.createMetaPropertyNode());
|
|
} else {
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
}
|
|
|
|
MetaNode node = this->createNode();
|
|
ASTNode callee = this->isolateCoverGrammar(builder, &Parser::parseLeftHandSideExpression<ASTBuilder>);
|
|
ASTNodeVector args;
|
|
if (this->match(LeftParenthesis)) {
|
|
args = this->parseArguments(builder);
|
|
}
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
|
|
return this->finalize(node, builder.createNewExpressionNode(callee.get(), std::move(args)));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseLeftHandSideExpressionAllowCall(ASTBuilder& builder)
|
|
{
|
|
ALLOC_TOKEN(startToken);
|
|
*startToken = this->lookahead;
|
|
bool previousAllowIn = this->context->allowIn;
|
|
this->context->allowIn = true;
|
|
|
|
ASTNode exprNode;
|
|
|
|
if (this->context->inFunctionBody && this->matchKeyword(SuperKeyword)) {
|
|
throwIfSuperOperationIsNotAllowed();
|
|
scopeContexts.back()->m_hasSuperOrNewTarget = true;
|
|
|
|
exprNode = this->finalize(this->createNode(), builder.createSuperExpressionNode(this->lookahead.valuePunctuatorKind == LeftParenthesis));
|
|
} else if (this->matchKeyword(NewKeyword)) {
|
|
exprNode = this->inheritCoverGrammar(builder, &Parser::parseNewExpression<ASTBuilder>);
|
|
} else {
|
|
exprNode = this->inheritCoverGrammar(builder, &Parser::parsePrimaryExpression<ASTBuilder>);
|
|
}
|
|
|
|
while (true) {
|
|
bool isPunctuatorTokenLookahead = this->lookahead.type == Token::PunctuatorToken;
|
|
if (isPunctuatorTokenLookahead) {
|
|
if (this->lookahead.valuePunctuatorKind == Period) {
|
|
this->context->isBindingElement = false;
|
|
this->context->isAssignmentTarget = true;
|
|
this->nextToken();
|
|
bool trackUsingNamesBefore = this->trackUsingNames;
|
|
this->trackUsingNames = false;
|
|
ASTNode property = this->parseIdentifierName(builder);
|
|
exprNode = this->finalize(this->startNode(startToken), builder.createMemberExpressionNode(exprNode.get(), property.get(), true));
|
|
this->trackUsingNames = trackUsingNamesBefore;
|
|
} else if (this->lookahead.valuePunctuatorKind == LeftParenthesis) {
|
|
this->context->isBindingElement = false;
|
|
this->context->isAssignmentTarget = false;
|
|
// check callee of CallExpressionNode
|
|
if (exprNode->isIdentifier() && exprNode->asIdentifier()->name() == escargotContext->staticStrings().eval) {
|
|
scopeContexts.back()->m_hasEval = true;
|
|
}
|
|
exprNode = this->finalize(this->startNode(startToken), builder.createCallExpressionNode(exprNode.get(), this->parseArguments(builder)));
|
|
} else if (this->lookahead.valuePunctuatorKind == LeftSquareBracket) {
|
|
this->context->isBindingElement = false;
|
|
this->context->isAssignmentTarget = true;
|
|
this->nextToken();
|
|
ASTNode property = this->isolateCoverGrammar(builder, &Parser::parseExpression<ASTBuilder>);
|
|
exprNode = this->finalize(this->startNode(startToken), builder.createMemberExpressionNode(exprNode.get(), property.get(), false));
|
|
this->expect(RightSquareBracket);
|
|
} else {
|
|
break;
|
|
}
|
|
} else if (this->lookahead.type == Token::TemplateToken && this->lookahead.valueTemplate->head) {
|
|
ASTNode quasi = this->parseTemplateLiteral(builder);
|
|
// FIXME convertTaggedTemplateExpressionToCallExpression
|
|
exprNode = builder.convertTaggedTemplateExpressionToCallExpression(this->finalize(this->startNode(startToken), builder.createTaggedTemplateExpressionNode(exprNode.get(), quasi.get())), scopeContexts.back(), escargotContext->staticStrings().raw);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
this->context->allowIn = previousAllowIn;
|
|
|
|
return exprNode.release();
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseSuper(ASTBuilder& builder)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
this->expectKeyword(SuperKeyword);
|
|
if (!this->match(LeftSquareBracket) && !this->match(Period)) {
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
|
|
scopeContexts.back()->m_hasSuperOrNewTarget = true;
|
|
|
|
return this->finalize(node, builder.createSuperExpressionNode(false));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseLeftHandSideExpression(ASTBuilder& builder)
|
|
{
|
|
// assert(this->context->allowIn, 'callee of new expression always allow in keyword.');
|
|
ASSERT(this->context->allowIn);
|
|
|
|
ASTNode exprNode;
|
|
|
|
if (this->matchKeyword(SuperKeyword) && this->context->inFunctionBody) {
|
|
exprNode = this->parseSuper(builder);
|
|
} else if (this->matchKeyword(NewKeyword)) {
|
|
exprNode = this->inheritCoverGrammar(builder, &Parser::parseNewExpression<ASTBuilder>);
|
|
} else {
|
|
exprNode = this->inheritCoverGrammar(builder, &Parser::parsePrimaryExpression<ASTBuilder>);
|
|
}
|
|
|
|
MetaNode node = this->startNode(&this->lookahead);
|
|
|
|
while (true) {
|
|
if (this->match(LeftSquareBracket)) {
|
|
this->context->isBindingElement = false;
|
|
this->context->isAssignmentTarget = true;
|
|
this->expect(LeftSquareBracket);
|
|
ASTNode property = this->isolateCoverGrammar(builder, &Parser::parseExpression<ASTBuilder>);
|
|
exprNode = this->finalize(node, builder.createMemberExpressionNode(exprNode.get(), property.get(), false));
|
|
this->expect(RightSquareBracket);
|
|
} else if (this->match(Period)) {
|
|
this->context->isBindingElement = false;
|
|
this->context->isAssignmentTarget = true;
|
|
this->expect(Period);
|
|
bool trackUsingNamesBefore = this->trackUsingNames;
|
|
this->trackUsingNames = false;
|
|
ASTNode property = this->parseIdentifierName(builder);
|
|
exprNode = this->finalize(node, builder.createMemberExpressionNode(exprNode.get(), property.get(), true));
|
|
this->trackUsingNames = trackUsingNamesBefore;
|
|
} else if (this->lookahead.type == Token::TemplateToken && this->lookahead.valueTemplate->head) {
|
|
ASTNode quasi = this->parseTemplateLiteral(builder);
|
|
// FIXME convertTaggedTemplateExpressionToCallExpression
|
|
exprNode = builder.convertTaggedTemplateExpressionToCallExpression(this->finalize(node, builder.createTaggedTemplateExpressionNode(exprNode.get(), quasi.get())), scopeContexts.back(), escargotContext->staticStrings().raw);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return exprNode.release();
|
|
}
|
|
|
|
// ECMA-262 12.4 Update Expressions
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseUpdateExpression(ASTBuilder& builder)
|
|
{
|
|
ASTNode exprNode;
|
|
ALLOC_TOKEN(startToken);
|
|
*startToken = this->lookahead;
|
|
|
|
if (this->match(PlusPlus) || this->match(MinusMinus)) {
|
|
bool isPlus = this->match(PlusPlus);
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
|
|
exprNode = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression<ASTBuilder>);
|
|
if (exprNode->isLiteral() || exprNode->type() == ASTNodeType::ThisExpression) {
|
|
this->throwError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
|
|
}
|
|
if (this->context->strict && exprNode->type() == Identifier && this->scanner->isRestrictedWord(exprNode->asIdentifier()->name())) {
|
|
this->throwError(Messages::StrictLHSPrefix);
|
|
}
|
|
|
|
if (!this->context->isAssignmentTarget && this->context->strict) {
|
|
this->throwError(Messages::InvalidLHSInAssignment);
|
|
}
|
|
|
|
MetaNode node = this->startNode(startToken);
|
|
if (isPlus) {
|
|
exprNode = this->finalize(node, builder.createUpdateExpressionIncrementPrefixNode(exprNode.get()));
|
|
} else {
|
|
exprNode = this->finalize(node, builder.createUpdateExpressionDecrementPrefixNode(exprNode.get()));
|
|
}
|
|
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else {
|
|
exprNode = this->inheritCoverGrammar(builder, &Parser::parseLeftHandSideExpressionAllowCall<ASTBuilder>);
|
|
if (!this->hasLineTerminator && this->lookahead.type == Token::PunctuatorToken && (this->match(PlusPlus) || this->match(MinusMinus))) {
|
|
bool isPlus = this->match(PlusPlus);
|
|
if (exprNode->isLiteral() || exprNode->type() == ASTNodeType::ThisExpression) {
|
|
this->throwError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
|
|
}
|
|
if (this->context->strict && exprNode->isIdentifier() && this->scanner->isRestrictedWord(exprNode->asIdentifier()->name())) {
|
|
this->throwError(Messages::StrictLHSPostfix);
|
|
}
|
|
if (!this->context->isAssignmentTarget && this->context->strict) {
|
|
this->throwError(Messages::InvalidLHSInAssignment);
|
|
}
|
|
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
this->nextToken();
|
|
|
|
if (isPlus) {
|
|
exprNode = this->finalize(this->startNode(startToken), builder.createUpdateExpressionIncrementPostfixNode(exprNode.get()));
|
|
} else {
|
|
exprNode = this->finalize(this->startNode(startToken), builder.createUpdateExpressionDecrementPostfixNode(exprNode.get()));
|
|
}
|
|
}
|
|
}
|
|
|
|
return exprNode.release();
|
|
}
|
|
|
|
// ECMA-262 12.5 Unary Operators
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseUnaryExpression(ASTBuilder& builder)
|
|
{
|
|
if (this->lookahead.type == Token::PunctuatorToken) {
|
|
auto punctuatorsKind = this->lookahead.valuePunctuatorKind;
|
|
ASTNode exprNode;
|
|
|
|
if (punctuatorsKind == Plus) {
|
|
this->nextToken();
|
|
MetaNode node = this->startNode(&this->lookahead);
|
|
ASTNode subExpr = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression<ASTBuilder>);
|
|
exprNode = this->finalize(node, builder.createUnaryExpressionPlusNode(subExpr.get()));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
return exprNode.release();
|
|
} else if (punctuatorsKind == Minus) {
|
|
this->nextToken();
|
|
MetaNode node = this->startNode(&this->lookahead);
|
|
ASTNode subExpr = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression<ASTBuilder>);
|
|
exprNode = this->finalize(node, builder.createUnaryExpressionMinusNode(subExpr.get()));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
return exprNode.release();
|
|
} else if (punctuatorsKind == Wave) {
|
|
this->nextToken();
|
|
MetaNode node = this->startNode(&this->lookahead);
|
|
ASTNode subExpr = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression<ASTBuilder>);
|
|
exprNode = this->finalize(node, builder.createUnaryExpressionBitwiseNotNode(subExpr.get()));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
return exprNode.release();
|
|
} else if (punctuatorsKind == ExclamationMark) {
|
|
this->nextToken();
|
|
MetaNode node = this->startNode(&this->lookahead);
|
|
ASTNode subExpr = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression<ASTBuilder>);
|
|
exprNode = this->finalize(node, builder.createUnaryExpressionLogicalNotNode(subExpr.get()));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
return exprNode.release();
|
|
}
|
|
}
|
|
|
|
bool isKeyword = this->lookahead.type == Token::KeywordToken;
|
|
|
|
if (isKeyword) {
|
|
ASTNode exprNode;
|
|
|
|
if (this->lookahead.valueKeywordKind == DeleteKeyword) {
|
|
this->nextToken();
|
|
MetaNode node = this->startNode(&this->lookahead);
|
|
ASTNode subExpr = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression<ASTBuilder>);
|
|
|
|
if (this->context->strict && subExpr->isIdentifier()) {
|
|
this->throwError(Messages::StrictDelete);
|
|
}
|
|
|
|
exprNode = this->finalize(node, builder.createUnaryExpressionDeleteNode(subExpr.get()));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
|
|
return exprNode.release();
|
|
} else if (this->lookahead.valueKeywordKind == VoidKeyword) {
|
|
this->nextToken();
|
|
MetaNode node = this->startNode(&this->lookahead);
|
|
ASTNode subExpr = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression<ASTBuilder>);
|
|
exprNode = this->finalize(node, builder.createUnaryExpressionVoidNode(subExpr.get()));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
|
|
return exprNode.release();
|
|
} else if (this->lookahead.valueKeywordKind == TypeofKeyword) {
|
|
this->nextToken();
|
|
|
|
MetaNode node = this->startNode(&this->lookahead);
|
|
ASTNode subExpr = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression<ASTBuilder>);
|
|
exprNode = this->finalize(node, builder.createUnaryExpressionTypeOfNode(subExpr.get()));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
|
|
return exprNode.release();
|
|
}
|
|
}
|
|
|
|
return this->parseUpdateExpression(builder);
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseExponentiationExpression(ASTBuilder& builder)
|
|
{
|
|
ALLOC_TOKEN(startToken);
|
|
*startToken = this->lookahead;
|
|
ASTNode expr = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression<ASTBuilder>);
|
|
// TODO
|
|
/*
|
|
if (expr->type != Syntax.UnaryExpression && this->match('**')) {
|
|
this->nextToken();
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
const left = expr;
|
|
const right = this->isolateCoverGrammar(this->parseExponentiationExpression);
|
|
expr = this->finalize(this->startNode(startToken), new Node.BinaryExpression('**', left, right));
|
|
}*/
|
|
|
|
return expr;
|
|
}
|
|
|
|
// ECMA-262 12.6 Exponentiation Operators
|
|
// ECMA-262 12.7 Multiplicative Operators
|
|
// ECMA-262 12.8 Additive Operators
|
|
// ECMA-262 12.9 Bitwise Shift Operators
|
|
// ECMA-262 12.10 Relational Operators
|
|
// ECMA-262 12.11 Equality Operators
|
|
// ECMA-262 12.12 Binary Bitwise Operators
|
|
// ECMA-262 12.13 Binary Logical Operators
|
|
|
|
int binaryPrecedence(const Scanner::ScannerResult* token)
|
|
{
|
|
ASSERT(token != nullptr);
|
|
if (LIKELY(token->type == Token::PunctuatorToken)) {
|
|
switch (token->valuePunctuatorKind) {
|
|
case Substitution:
|
|
return 0;
|
|
case LogicalOr:
|
|
return 1;
|
|
case LogicalAnd:
|
|
return 2;
|
|
case BitwiseOr:
|
|
return 3;
|
|
case BitwiseXor:
|
|
return 4;
|
|
case BitwiseAnd:
|
|
return 5;
|
|
case Equal:
|
|
return 6;
|
|
case NotEqual:
|
|
return 6;
|
|
case StrictEqual:
|
|
return 6;
|
|
case NotStrictEqual:
|
|
return 6;
|
|
case RightInequality:
|
|
return 7;
|
|
case LeftInequality:
|
|
return 7;
|
|
case RightInequalityEqual:
|
|
return 7;
|
|
case LeftInequalityEqual:
|
|
return 7;
|
|
case LeftShift:
|
|
return 8;
|
|
case RightShift:
|
|
return 8;
|
|
case UnsignedRightShift:
|
|
return 8;
|
|
case Plus:
|
|
return 9;
|
|
case Minus:
|
|
return 9;
|
|
case Multiply:
|
|
return 11;
|
|
case Divide:
|
|
return 11;
|
|
case Mod:
|
|
return 11;
|
|
default:
|
|
return 0;
|
|
}
|
|
} else if (token->type == Token::KeywordToken) {
|
|
if (token->valueKeywordKind == InKeyword) {
|
|
return this->context->allowIn ? 7 : 0;
|
|
} else if (token->valueKeywordKind == InstanceofKeyword) {
|
|
return 7;
|
|
}
|
|
} else {
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseBinaryExpression(ASTBuilder& builder)
|
|
{
|
|
ALLOC_TOKEN(startToken);
|
|
*startToken = this->lookahead;
|
|
|
|
ASTNode expr = this->inheritCoverGrammar(builder, &Parser::parseExponentiationExpression<ASTBuilder>);
|
|
|
|
ALLOC_TOKEN(token);
|
|
*token = this->lookahead;
|
|
int prec = this->binaryPrecedence(token);
|
|
if (prec > 0) {
|
|
this->nextToken();
|
|
|
|
token->prec = prec;
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
|
|
// FIXME optimize ScannerResult vector and its related operation
|
|
ScannerResultVector markers;
|
|
markers.reserve(120);
|
|
markers.push_back(*startToken);
|
|
markers.push_back(this->lookahead);
|
|
ASTNode left = expr;
|
|
ASTNode right = this->isolateCoverGrammar(builder, &Parser::parseExponentiationExpression<ASTBuilder>);
|
|
|
|
ASTNodeVector stack;
|
|
ScannerResultVector tokenStack;
|
|
tokenStack.reserve(120);
|
|
|
|
stack.push_back(left);
|
|
stack.push_back(right);
|
|
tokenStack.push_back(*token);
|
|
|
|
while (true) {
|
|
prec = this->binaryPrecedence(&this->lookahead);
|
|
if (prec <= 0) {
|
|
break;
|
|
}
|
|
|
|
// Reduce: make a binary expression from the three topmost entries.
|
|
while ((stack.size() > 1) && (prec <= tokenStack.back().prec)) {
|
|
right = stack.back();
|
|
stack.pop_back();
|
|
Scanner::ScannerResult operator_ = tokenStack.back();
|
|
tokenStack.pop_back();
|
|
left = stack.back();
|
|
stack.pop_back();
|
|
markers.pop_back();
|
|
MetaNode node = this->startNode(&markers.back());
|
|
auto e = this->finalize(node, finishBinaryExpression(builder, left.get(), right.get(), &operator_));
|
|
stack.push_back(e);
|
|
}
|
|
|
|
// Shift.
|
|
this->nextToken(token);
|
|
token->prec = prec;
|
|
tokenStack.push_back(*token);
|
|
markers.push_back(this->lookahead);
|
|
auto e = this->isolateCoverGrammar(builder, &Parser::parseExponentiationExpression<ASTBuilder>);
|
|
stack.push_back(e);
|
|
}
|
|
|
|
// Final reduce to clean-up the stack.
|
|
size_t i = stack.size() - 1;
|
|
expr = stack[i];
|
|
markers.pop_back();
|
|
while (i > 0) {
|
|
MetaNode node = this->startNode(&markers.back());
|
|
expr = this->finalize(node, finishBinaryExpression(builder, stack[i - 1].get(), expr.get(), &tokenStack.back()));
|
|
markers.pop_back();
|
|
tokenStack.pop_back();
|
|
i--;
|
|
}
|
|
RELEASE_ASSERT(i == 0);
|
|
}
|
|
|
|
return expr.release();
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTNodePtr finishBinaryExpression(ASTBuilder& builder, ASTNodePtr left, ASTNodePtr right, Scanner::ScannerResult* token)
|
|
{
|
|
if (token->type == Token::PunctuatorToken) {
|
|
PunctuatorKind oper = token->valuePunctuatorKind;
|
|
// Additive Operators
|
|
switch (oper) {
|
|
case Plus:
|
|
return builder.createBinaryExpressionPlusNode(left, right);
|
|
case Minus:
|
|
return builder.createBinaryExpressionMinusNode(left, right);
|
|
case LeftShift:
|
|
return builder.createBinaryExpressionLeftShiftNode(left, right);
|
|
case RightShift:
|
|
return builder.createBinaryExpressionSignedRightShiftNode(left, right);
|
|
case UnsignedRightShift:
|
|
return builder.createBinaryExpressionUnsignedRightShiftNode(left, right);
|
|
case Multiply:
|
|
return builder.createBinaryExpressionMultiplyNode(left, right);
|
|
case Divide:
|
|
return builder.createBinaryExpressionDivisionNode(left, right);
|
|
case Mod:
|
|
return builder.createBinaryExpressionModNode(left, right);
|
|
case LeftInequality:
|
|
return builder.createBinaryExpressionLessThanNode(left, right);
|
|
case RightInequality:
|
|
return builder.createBinaryExpressionGreaterThanNode(left, right);
|
|
case LeftInequalityEqual:
|
|
return builder.createBinaryExpressionLessThanOrEqualNode(left, right);
|
|
case RightInequalityEqual:
|
|
return builder.createBinaryExpressionGreaterThanOrEqualNode(left, right);
|
|
case Equal:
|
|
return builder.createBinaryExpressionEqualNode(left, right);
|
|
case NotEqual:
|
|
return builder.createBinaryExpressionNotEqualNode(left, right);
|
|
case StrictEqual:
|
|
return builder.createBinaryExpressionStrictEqualNode(left, right);
|
|
case NotStrictEqual:
|
|
return builder.createBinaryExpressionNotStrictEqualNode(left, right);
|
|
case BitwiseAnd:
|
|
return builder.createBinaryExpressionBitwiseAndNode(left, right);
|
|
case BitwiseXor:
|
|
return builder.createBinaryExpressionBitwiseXorNode(left, right);
|
|
case BitwiseOr:
|
|
return builder.createBinaryExpressionBitwiseOrNode(left, right);
|
|
case LogicalOr:
|
|
return builder.createBinaryExpressionLogicalOrNode(left, right);
|
|
case LogicalAnd:
|
|
return builder.createBinaryExpressionLogicalAndNode(left, right);
|
|
default:
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
} else {
|
|
ASSERT(token->type == Token::KeywordToken);
|
|
switch (token->valueKeywordKind) {
|
|
case InKeyword:
|
|
return builder.createBinaryExpressionInNode(left, right);
|
|
case KeywordKind::InstanceofKeyword:
|
|
return builder.createBinaryExpressionInstanceOfNode(left, right);
|
|
default:
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
}
|
|
}
|
|
|
|
// ECMA-262 12.14 Conditional Operator
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseConditionalExpression(ASTBuilder& builder)
|
|
{
|
|
ALLOC_TOKEN(startToken);
|
|
*startToken = this->lookahead;
|
|
ASTNode exprNode;
|
|
|
|
exprNode = this->inheritCoverGrammar(builder, &Parser::parseBinaryExpression<ASTBuilder>);
|
|
|
|
if (this->match(GuessMark)) {
|
|
ASTNode consequent;
|
|
|
|
this->nextToken();
|
|
|
|
bool previousAllowIn = this->context->allowIn;
|
|
this->context->allowIn = true;
|
|
consequent = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
|
|
this->context->allowIn = previousAllowIn;
|
|
|
|
this->expect(Colon);
|
|
ASTNode alternate = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
|
|
exprNode = this->finalize(this->startNode(startToken), builder.createConditionalExpressionNode(exprNode.get(), consequent.get(), alternate.get()));
|
|
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
}
|
|
|
|
return exprNode.release();
|
|
}
|
|
|
|
// ECMA-262 12.15 Assignment Operators
|
|
|
|
// FIXME remove these reparseConditionalExpression function
|
|
RefPtr<Node> reparseConditionalExpression(Marker startMarker, RefPtr<Node> expr)
|
|
{
|
|
return expr;
|
|
}
|
|
|
|
RefPtr<Node> reparseConditionalExpression(Marker startMarker, SyntaxNode expr)
|
|
{
|
|
this->scanner->index = startMarker.index;
|
|
this->scanner->lineNumber = startMarker.lineNumber;
|
|
this->scanner->lineStart = startMarker.lineStart;
|
|
this->nextToken();
|
|
|
|
NodeGenerator tempBuilder;
|
|
return this->parseConditionalExpression(tempBuilder);
|
|
}
|
|
|
|
template <class ASTBuilder, bool checkLeftHasRestrictedWord>
|
|
ASTPassNode parseAssignmentExpression(ASTBuilder& builder)
|
|
{
|
|
ASTNode exprNode;
|
|
|
|
if (!this->context->allowYield && this->matchKeyword(YieldKeyword)) {
|
|
exprNode = this->parseYieldExpression(builder);
|
|
} else {
|
|
ALLOC_TOKEN(startToken);
|
|
*startToken = this->lookahead;
|
|
ALLOC_TOKEN(token);
|
|
*token = *startToken;
|
|
ASTNodeType type;
|
|
MetaNode startNode = this->createNode();
|
|
Marker startMarker = this->lastMarker;
|
|
|
|
exprNode = this->parseConditionalExpression(builder);
|
|
type = exprNode->type();
|
|
|
|
/*
|
|
if (token.type === Token.Identifier && (token.lineNumber === this.lookahead.lineNumber) && token.value === 'async' && (this.lookahead.type === Token.Identifier)) {
|
|
const arg = this.primaryExpression<Parse>();
|
|
expr = {
|
|
type: ArrowParameterPlaceHolder,
|
|
params: [arg],
|
|
async: true
|
|
};
|
|
} */
|
|
|
|
if (type == ArrowParameterPlaceHolder || this->match(Arrow)) {
|
|
// ECMA-262 14.2 Arrow Function Definitions
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
|
|
// FIXME remove repeated parseConditionalExpression call
|
|
RefPtr<Node> exprParamNode = reparseConditionalExpression(startMarker, exprNode);
|
|
|
|
pushScopeContext(AtomicString());
|
|
|
|
ParseFormalParametersResult list;
|
|
|
|
// FIXME reinterpretAsCoverFormalsList
|
|
if (type == Identifier) {
|
|
ASSERT(exprParamNode->isIdentifier());
|
|
this->validateParam(list, &this->lookahead, exprParamNode->asIdentifier()->name());
|
|
list.params.push_back(exprParamNode);
|
|
list.valid = true;
|
|
} else {
|
|
this->scanner->index = startMarker.index;
|
|
this->scanner->lineNumber = startMarker.lineNumber;
|
|
this->scanner->lineStart = startMarker.lineStart;
|
|
this->nextToken();
|
|
this->expect(LeftParenthesis);
|
|
scopeContexts.back()->m_hasArrowParameterPlaceHolder = true;
|
|
|
|
list = this->parseFormalParameters();
|
|
}
|
|
|
|
exprNode.release();
|
|
|
|
// FIXME remove validity check?
|
|
if (list.valid) {
|
|
if (this->hasLineTerminator) {
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
this->context->firstCoverInitializedNameError.reset();
|
|
|
|
bool previousStrict = this->context->strict;
|
|
bool previousAllowYield = this->context->allowYield;
|
|
bool previousInArrowFunction = this->context->inArrowFunction;
|
|
|
|
this->context->allowYield = true;
|
|
this->context->inArrowFunction = true;
|
|
|
|
scopeContexts.back()->m_allowSuperCall = this->context->allowSuperCall;
|
|
scopeContexts.back()->m_allowSuperProperty = this->context->allowSuperProperty;
|
|
|
|
scopeContexts.back()->m_paramsStartLOC.index = startNode.index;
|
|
scopeContexts.back()->m_paramsStartLOC.column = startNode.column;
|
|
scopeContexts.back()->m_paramsStartLOC.line = startNode.line;
|
|
|
|
extractNamesFromFunctionParams(list);
|
|
|
|
this->expect(Arrow);
|
|
|
|
MetaNode node = this->startNode(startToken);
|
|
MetaNode bodyStart = this->createNode();
|
|
|
|
if (this->match(LeftBrace)) {
|
|
this->parseFunctionSourceElements(builder);
|
|
} else {
|
|
if (this->isParsingSingleFunction) {
|
|
// when parsing for function call,
|
|
// assignmentExpression should parse(scan) only child arrow functions
|
|
ASSERT(this->childFunctionIndex > 0);
|
|
|
|
size_t realIndex = this->childFunctionIndex - 1;
|
|
this->childFunctionIndex++;
|
|
InterpretedCodeBlock* currentTarget = this->codeBlock->asInterpretedCodeBlock();
|
|
size_t orgIndex = this->lookahead.start;
|
|
|
|
StringView src = currentTarget->childBlocks()[realIndex]->bodySrc();
|
|
this->scanner->index = src.length() + orgIndex;
|
|
|
|
this->scanner->lineNumber = currentTarget->childBlocks()[realIndex]->sourceElementStart().line;
|
|
this->scanner->lineStart = currentTarget->childBlocks()[realIndex]->sourceElementStart().index - currentTarget->childBlocks()[realIndex]->sourceElementStart().column;
|
|
this->lookahead.lineNumber = this->scanner->lineNumber;
|
|
this->lookahead.lineStart = this->scanner->lineStart;
|
|
this->nextToken();
|
|
} else {
|
|
ASSERT(!this->isParsingSingleFunction);
|
|
LexicalBlockIndex lexicalBlockIndexBefore = this->lexicalBlockIndex;
|
|
LexicalBlockIndex lexicalBlockCountBefore = this->lexicalBlockCount;
|
|
this->lexicalBlockIndex = 0;
|
|
this->lexicalBlockCount = 0;
|
|
size_t oldSubCodeBlockIndex = this->subCodeBlockIndex;
|
|
this->subCodeBlockIndex = 0;
|
|
|
|
auto oldNameCallback = this->nameDeclaredCallback;
|
|
|
|
if (this->isParsingSingleFunction) {
|
|
ASSERT(this->childFunctionIndex > 0);
|
|
this->childFunctionIndex++;
|
|
}
|
|
|
|
// parsing arrow function body by SyntaxChecker only at this point
|
|
SyntaxChecker newBuilder;
|
|
this->isolateCoverGrammar(newBuilder, &Parser::parseAssignmentExpression<SyntaxChecker, false>);
|
|
|
|
this->subCodeBlockIndex = oldSubCodeBlockIndex;
|
|
this->lexicalBlockIndex = lexicalBlockIndexBefore;
|
|
this->lexicalBlockCount = lexicalBlockCountBefore;
|
|
this->nameDeclaredCallback = oldNameCallback;
|
|
|
|
scopeContexts.back()->m_bodyStartLOC.line = bodyStart.line;
|
|
scopeContexts.back()->m_bodyStartLOC.column = bodyStart.column;
|
|
scopeContexts.back()->m_bodyStartLOC.index = bodyStart.index;
|
|
|
|
scopeContexts.back()->m_bodyEndLOC.index = this->lastMarker.index;
|
|
#ifndef NDEBUG
|
|
scopeContexts.back()->m_bodyEndLOC.line = this->lastMarker.lineNumber;
|
|
scopeContexts.back()->m_bodyEndLOC.column = this->lastMarker.index - this->lastMarker.lineStart;
|
|
#endif
|
|
}
|
|
}
|
|
this->scopeContexts.back()->m_lexicalBlockIndexFunctionLocatedIn = this->lexicalBlockIndex;
|
|
|
|
if (this->context->strict && list.firstRestricted) {
|
|
this->throwUnexpectedToken(&list.firstRestricted, list.message);
|
|
}
|
|
if (this->context->strict && list.stricted) {
|
|
this->throwUnexpectedToken(&list.stricted, list.message);
|
|
}
|
|
|
|
this->scopeContexts.back()->m_isArrowFunctionExpression = true;
|
|
this->scopeContexts.back()->m_nodeType = ASTNodeType::ArrowFunctionExpression;
|
|
this->scopeContexts.back()->m_isGenerator = false;
|
|
exprNode = this->finalize(node, builder.createArrowFunctionExpressionNode(popScopeContext(node), subCodeBlockIndex)); //TODO
|
|
|
|
this->context->strict = previousStrict;
|
|
this->context->allowYield = previousAllowYield;
|
|
this->context->inArrowFunction = previousInArrowFunction;
|
|
}
|
|
} else {
|
|
// check if restricted words are used as target in array/object initializer
|
|
if (checkLeftHasRestrictedWord) {
|
|
if (this->context->strict && type == Identifier) {
|
|
AtomicString name;
|
|
name = exprNode->asIdentifier()->name();
|
|
if (this->scanner->isRestrictedWord(name)) {
|
|
this->context->hasRestrictedWordInArrayOrObjectInitializer = true;
|
|
}
|
|
}
|
|
}
|
|
if (this->matchAssign()) {
|
|
if (!this->context->isAssignmentTarget) {
|
|
if (type == ArrayExpression || type == ObjectExpression) {
|
|
this->throwError(Messages::InvalidLHSInAssignment);
|
|
}
|
|
if (this->context->strict) {
|
|
this->throwError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
|
|
}
|
|
}
|
|
|
|
if (this->context->strict) {
|
|
if (type == Identifier) {
|
|
AtomicString name = exprNode->asIdentifier()->name();
|
|
if (this->scanner->isRestrictedWord(name)) {
|
|
this->throwUnexpectedToken(token, Messages::StrictLHSAssignment);
|
|
}
|
|
if (this->scanner->isStrictModeReservedWord(name)) {
|
|
this->throwUnexpectedToken(token, Messages::StrictReservedWord);
|
|
}
|
|
} else if (type == ArrayExpression || type == ObjectExpression) {
|
|
if (this->context->hasRestrictedWordInArrayOrObjectInitializer) {
|
|
this->throwError(Messages::StrictLHSAssignment);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!this->match(Substitution)) {
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else {
|
|
exprNode = builder.reinterpretExpressionAsPattern(exprNode.get());
|
|
|
|
if (exprNode->isLiteral() || exprNode->type() == ASTNodeType::ThisExpression) {
|
|
this->throwError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
|
|
}
|
|
}
|
|
|
|
this->nextToken(token);
|
|
ASTNode rightNode;
|
|
ASTNodePtr exprResult;
|
|
|
|
rightNode = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
|
|
|
|
switch (token->valuePunctuatorKind) {
|
|
case Substitution:
|
|
exprResult = builder.createAssignmentExpressionSimpleNode(exprNode.get(), rightNode.get());
|
|
break;
|
|
case PlusEqual:
|
|
exprResult = builder.createAssignmentExpressionPlusNode(exprNode.get(), rightNode.get());
|
|
break;
|
|
case MinusEqual:
|
|
exprResult = builder.createAssignmentExpressionMinusNode(exprNode.get(), rightNode.get());
|
|
break;
|
|
case MultiplyEqual:
|
|
exprResult = builder.createAssignmentExpressionMultiplyNode(exprNode.get(), rightNode.get());
|
|
break;
|
|
case DivideEqual:
|
|
exprResult = builder.createAssignmentExpressionDivisionNode(exprNode.get(), rightNode.get());
|
|
break;
|
|
case ModEqual:
|
|
exprResult = builder.createAssignmentExpressionModNode(exprNode.get(), rightNode.get());
|
|
break;
|
|
case LeftShiftEqual:
|
|
exprResult = builder.createAssignmentExpressionLeftShiftNode(exprNode.get(), rightNode.get());
|
|
break;
|
|
case RightShiftEqual:
|
|
exprResult = builder.createAssignmentExpressionSignedRightShiftNode(exprNode.get(), rightNode.get());
|
|
break;
|
|
case UnsignedRightShiftEqual:
|
|
exprResult = builder.createAssignmentExpressionUnsignedRightShiftNode(exprNode.get(), rightNode.get());
|
|
break;
|
|
case BitwiseXorEqual:
|
|
exprResult = builder.createAssignmentExpressionBitwiseXorNode(exprNode.get(), rightNode.get());
|
|
break;
|
|
case BitwiseAndEqual:
|
|
exprResult = builder.createAssignmentExpressionBitwiseAndNode(exprNode.get(), rightNode.get());
|
|
break;
|
|
case BitwiseOrEqual:
|
|
exprResult = builder.createAssignmentExpressionBitwiseOrNode(exprNode.get(), rightNode.get());
|
|
break;
|
|
default:
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
exprNode = this->finalize(this->startNode(startToken), exprResult);
|
|
this->context->firstCoverInitializedNameError.reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
return exprNode.release();
|
|
}
|
|
|
|
// ECMA-262 12.16 Comma Operator
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseExpression(ASTBuilder& builder)
|
|
{
|
|
ALLOC_TOKEN(startToken);
|
|
*startToken = this->lookahead;
|
|
ASTNode exprNode;
|
|
|
|
exprNode = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
|
|
|
|
if (this->match(Comma)) {
|
|
ASTNodeVector expressions;
|
|
expressions.push_back(exprNode);
|
|
while (this->startMarker.index < this->scanner->length) {
|
|
if (!this->match(Comma)) {
|
|
break;
|
|
}
|
|
this->nextToken();
|
|
expressions.push_back(this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>));
|
|
}
|
|
|
|
exprNode = this->finalize(this->startNode(startToken), builder.createSequenceExpressionNode(std::move(expressions)));
|
|
}
|
|
|
|
return exprNode.release();
|
|
}
|
|
|
|
// ECMA-262 13.2 Block
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseStatementListItem(ASTBuilder& builder)
|
|
{
|
|
ASTNode statement;
|
|
this->context->isAssignmentTarget = true;
|
|
this->context->isBindingElement = true;
|
|
this->context->hasRestrictedWordInArrayOrObjectInitializer = false;
|
|
if (this->lookahead.type == KeywordToken) {
|
|
switch (this->lookahead.valueKeywordKind) {
|
|
case FunctionKeyword:
|
|
statement = this->parseFunctionDeclaration(builder);
|
|
break;
|
|
case ExportKeyword:
|
|
if (this->sourceType != Module) {
|
|
this->throwUnexpectedToken(&this->lookahead, Messages::IllegalExportDeclaration);
|
|
}
|
|
statement = this->parseExportDeclaration(builder);
|
|
break;
|
|
case ImportKeyword:
|
|
if (this->sourceType != Module) {
|
|
this->throwUnexpectedToken(&this->lookahead, Messages::IllegalImportDeclaration);
|
|
}
|
|
statement = this->parseImportDeclaration(builder);
|
|
break;
|
|
case ConstKeyword:
|
|
statement = this->parseVariableStatement(builder, KeywordKind::ConstKeyword);
|
|
break;
|
|
case LetKeyword:
|
|
statement = this->isLexicalDeclaration() ? this->parseLexicalDeclaration(builder, false) : this->parseStatement(builder);
|
|
break;
|
|
case ClassKeyword:
|
|
statement = this->parseClassDeclaration(builder);
|
|
break;
|
|
default:
|
|
statement = this->parseStatement(builder);
|
|
break;
|
|
}
|
|
} else {
|
|
statement = this->parseStatement(builder);
|
|
}
|
|
|
|
return statement.release();
|
|
}
|
|
|
|
struct ParserBlockContext {
|
|
size_t lexicalBlockCountBefore;
|
|
size_t lexicalBlockIndexBefore;
|
|
size_t childLexicalBlockIndex;
|
|
|
|
ParserBlockContext()
|
|
: lexicalBlockCountBefore(SIZE_MAX)
|
|
, lexicalBlockIndexBefore(SIZE_MAX)
|
|
, childLexicalBlockIndex(SIZE_MAX)
|
|
{
|
|
}
|
|
};
|
|
|
|
ParserBlockContext openBlock()
|
|
{
|
|
if (UNLIKELY(this->lexicalBlockCount == LEXICAL_BLOCK_INDEX_MAX - 1)) {
|
|
this->throwError("too many lexical blocks in script", String::emptyString, String::emptyString, ErrorObject::RangeError);
|
|
}
|
|
|
|
ParserBlockContext ctx;
|
|
this->lexicalBlockCount++;
|
|
ctx.lexicalBlockCountBefore = this->lexicalBlockCount;
|
|
ctx.lexicalBlockIndexBefore = this->lexicalBlockIndex;
|
|
ctx.childLexicalBlockIndex = this->lexicalBlockCount;
|
|
|
|
this->scopeContexts.back()->insertBlockScope(ctx.childLexicalBlockIndex, this->lexicalBlockIndex,
|
|
ExtendedNodeLOC(this->lastMarker.lineNumber, this->lastMarker.index - this->lastMarker.lineStart + 1, this->lastMarker.index));
|
|
this->lexicalBlockIndex = ctx.childLexicalBlockIndex;
|
|
|
|
return ctx;
|
|
}
|
|
|
|
void closeBlock(ParserBlockContext& ctx)
|
|
{
|
|
if (this->isParsingSingleFunction) {
|
|
bool finded = false;
|
|
for (size_t i = 0; i < this->codeBlock->asInterpretedCodeBlock()->blockInfos().size(); i++) {
|
|
if (this->codeBlock->asInterpretedCodeBlock()->blockInfos()[i]->m_blockIndex == ctx.childLexicalBlockIndex) {
|
|
finded = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!finded) {
|
|
ctx.childLexicalBlockIndex = LEXICAL_BLOCK_INDEX_MAX;
|
|
}
|
|
} else {
|
|
// if there is no new variable in this block, merge this block into parent block
|
|
auto currentFunctionScope = this->scopeContexts.back();
|
|
auto blockContext = currentFunctionScope->findBlockFromBackward(this->lexicalBlockIndex);
|
|
if (this->lexicalBlockIndex != 0 && blockContext->m_names.size() == 0) {
|
|
const auto currentBlockIndex = this->lexicalBlockIndex;
|
|
LexicalBlockIndex parentBlockIndex = blockContext->m_parentBlockIndex;
|
|
|
|
bool isThereFunctionExists = false;
|
|
for (size_t i = 0; i < currentFunctionScope->m_childScopes.size(); i++) {
|
|
if (currentFunctionScope->m_childScopes[i]->m_nodeType == ASTNodeType::FunctionDeclaration
|
|
&& currentFunctionScope->m_childScopes[i]->m_lexicalBlockIndexFunctionLocatedIn == currentBlockIndex) {
|
|
isThereFunctionExists = true;
|
|
}
|
|
}
|
|
|
|
if (!isThereFunctionExists) { // block collapse start
|
|
for (size_t i = 0; i < currentFunctionScope->m_childBlockScopes.size(); i++) {
|
|
if (currentFunctionScope->m_childBlockScopes[i]->m_parentBlockIndex == currentBlockIndex) {
|
|
currentFunctionScope->m_childBlockScopes[i]->m_parentBlockIndex = parentBlockIndex;
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < currentFunctionScope->m_childScopes.size(); i++) {
|
|
if (currentFunctionScope->m_childScopes[i]->m_lexicalBlockIndexFunctionLocatedIn == currentBlockIndex) {
|
|
currentFunctionScope->m_childScopes[i]->m_lexicalBlockIndexFunctionLocatedIn = parentBlockIndex;
|
|
}
|
|
}
|
|
|
|
auto parentBlockContext = currentFunctionScope->findBlockFromBackward(parentBlockIndex);
|
|
for (size_t i = 0; i < blockContext->m_usingNames.size(); i++) {
|
|
AtomicString name = blockContext->m_usingNames[i];
|
|
if (VectorUtil::findInVector(parentBlockContext->m_usingNames, name) == VectorUtil::invalidIndex) {
|
|
parentBlockContext->m_usingNames.push_back(name);
|
|
}
|
|
}
|
|
|
|
// remove current block context from function context
|
|
for (size_t i = 0; i < currentFunctionScope->m_childBlockScopes.size(); i++) {
|
|
if (currentFunctionScope->m_childBlockScopes[i]->m_blockIndex == currentBlockIndex) {
|
|
currentFunctionScope->m_childBlockScopes.erase(i);
|
|
break;
|
|
}
|
|
}
|
|
ctx.childLexicalBlockIndex = LEXICAL_BLOCK_INDEX_MAX;
|
|
}
|
|
}
|
|
}
|
|
|
|
this->lexicalBlockIndex = ctx.lexicalBlockIndexBefore;
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseBlock(ASTBuilder& builder)
|
|
{
|
|
this->expect(LeftBrace);
|
|
ASTStatementContainer block;
|
|
ASTStatementNodePtr referNode = nullptr;
|
|
|
|
ParserBlockContext blockContext = openBlock();
|
|
|
|
bool allowLexicalDeclarationBefore = this->context->allowLexicalDeclaration;
|
|
this->context->allowLexicalDeclaration = true;
|
|
|
|
block = builder.createStatementContainer();
|
|
while (true) {
|
|
if (this->match(RightBrace)) {
|
|
break;
|
|
}
|
|
referNode = block->appendChild(this->parseStatementListItem(builder).get(), referNode);
|
|
}
|
|
this->expect(RightBrace);
|
|
|
|
this->context->allowLexicalDeclaration = allowLexicalDeclarationBefore;
|
|
|
|
closeBlock(blockContext);
|
|
|
|
MetaNode node = this->createNode();
|
|
return this->finalize(node, builder.createBlockStatementNode(block.get(), blockContext.childLexicalBlockIndex));
|
|
}
|
|
|
|
// ECMA-262 13.3.1 Let and Const Declarations
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseLexicalBinding(ASTBuilder& builder, KeywordKind kind, bool inFor)
|
|
{
|
|
auto node = this->createNode();
|
|
ScannerResultVector params;
|
|
ASTNode idNode;
|
|
bool isIdentifier;
|
|
AtomicString name;
|
|
|
|
idNode = this->parsePattern(builder, params, kind, true);
|
|
isIdentifier = (idNode->type() == Identifier);
|
|
if (isIdentifier) {
|
|
name = idNode->asIdentifier()->name();
|
|
}
|
|
|
|
// ECMA-262 12.2.1
|
|
if (this->context->strict && isIdentifier && this->scanner->isRestrictedWord(name)) {
|
|
this->throwError(Messages::StrictVarName);
|
|
}
|
|
|
|
ASTNode init;
|
|
if (kind == KeywordKind::ConstKeyword) {
|
|
if (!this->matchKeyword(KeywordKind::InKeyword) && !this->matchContextualKeyword("of")) {
|
|
this->expect(Substitution);
|
|
init = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
|
|
}
|
|
} else if ((!inFor && !isIdentifier) || this->match(Substitution)) {
|
|
this->expect(Substitution);
|
|
init = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
|
|
}
|
|
|
|
return this->finalize(node, builder.createVariableDeclaratorNode(kind, idNode.get(), init.get()));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTNodeVector parseBindingList(ASTBuilder& builder, KeywordKind kind, bool inFor)
|
|
{
|
|
ASTNodeVector list;
|
|
list.push_back(this->parseLexicalBinding(builder, kind, inFor));
|
|
while (this->match(Comma)) {
|
|
this->nextToken();
|
|
list.push_back(this->parseLexicalBinding(builder, kind, inFor));
|
|
}
|
|
return list;
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseLexicalDeclaration(ASTBuilder& builder, bool inFor)
|
|
{
|
|
auto node = this->createNode();
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
auto kind = token->valueKeywordKind;
|
|
|
|
ASTNodeVector declarations;
|
|
declarations = this->parseBindingList(builder, kind, inFor);
|
|
|
|
this->consumeSemicolon();
|
|
|
|
return this->finalize(node, builder.createVariableDeclarationNode(std::move(declarations), kind));
|
|
}
|
|
|
|
bool isLexicalDeclaration()
|
|
{
|
|
auto previousIndex = this->scanner->index;
|
|
auto previousLineNumber = this->scanner->lineNumber;
|
|
auto previousLineStart = this->scanner->lineStart;
|
|
this->collectComments();
|
|
ALLOC_TOKEN(next);
|
|
this->scanner->lex(next);
|
|
this->scanner->index = previousIndex;
|
|
this->scanner->lineNumber = previousLineNumber;
|
|
this->scanner->lineStart = previousLineStart;
|
|
|
|
return (next->type == Token::IdentifierToken) || (next->type == Token::PunctuatorToken && next->valuePunctuatorKind == PunctuatorKind::LeftSquareBracket) || (next->type == Token::PunctuatorToken && next->valuePunctuatorKind == PunctuatorKind::LeftBrace) || (next->type == Token::KeywordToken && next->valueKeywordKind == KeywordKind::LetKeyword) || (next->type == Token::KeywordToken && next->valueKeywordKind == KeywordKind::YieldKeyword);
|
|
}
|
|
|
|
// ECMA-262 13.3.2 Variable Statement
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseVariableIdentifier(ASTBuilder& builder, KeywordKind kind = KeywordKindEnd, bool isExplicitVariableDeclaration = false)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
if (token->type == Token::KeywordToken && token->valueKeywordKind == YieldKeyword) {
|
|
if (this->context->strict) {
|
|
this->throwUnexpectedToken(token, Messages::StrictReservedWord);
|
|
}
|
|
if (!this->context->allowYield) {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
} else if (token->type != Token::IdentifierToken) {
|
|
if (this->context->strict && token->type == Token::KeywordToken && this->scanner->isStrictModeReservedWord(token->relatedSource())) {
|
|
this->throwUnexpectedToken(token, Messages::StrictReservedWord);
|
|
} else {
|
|
if (this->context->strict || token->relatedSource() != "let" || (kind != VarKeyword)) {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
}
|
|
} else if (this->sourceType == Module && token->type == Token::IdentifierToken && token->relatedSource() == "await") {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
|
|
ASTIdentifierNode id = finishIdentifier(builder, token);
|
|
|
|
if (kind == KeywordKind::VarKeyword || kind == KeywordKind::LetKeyword || kind == KeywordKind::ConstKeyword) {
|
|
addDeclaredNameIntoContext(id->name(), this->lexicalBlockIndex, kind, isExplicitVariableDeclaration);
|
|
}
|
|
|
|
return this->finalize(node, id);
|
|
}
|
|
|
|
void addDeclaredNameIntoContext(AtomicString name, LexicalBlockIndex blockIndex, KeywordKind kind, bool isExplicitVariableDeclaration = false)
|
|
{
|
|
ASSERT(kind == VarKeyword || kind == LetKeyword || kind == ConstKeyword);
|
|
|
|
if (!this->isParsingSingleFunction) {
|
|
/*
|
|
we need this bunch of code for tolerate this error(we consider variable 'e' as lexically declared)
|
|
try { } catch(e) { var e; }
|
|
*/
|
|
if (this->context->inCatchClause && isExplicitVariableDeclaration && kind == KeywordKind::VarKeyword) {
|
|
for (size_t i = 0; i < this->context->catchClauseSimplyDeclaredVariableNames.size(); i++) {
|
|
if (this->context->catchClauseSimplyDeclaredVariableNames[i] == name) {
|
|
this->scopeContexts.back()->insertVarName(name, blockIndex, true, kind == VarKeyword);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
/* code end */
|
|
|
|
if (!this->scopeContexts.back()->canDeclareName(name, blockIndex, kind == VarKeyword)) {
|
|
this->throwError(Messages::Redeclaration, new ASCIIString("Identifier"), name.string());
|
|
}
|
|
if (kind == VarKeyword) {
|
|
this->scopeContexts.back()->insertVarName(name, blockIndex, true, true);
|
|
} else {
|
|
this->scopeContexts.back()->insertNameAtBlock(name, blockIndex, kind == ConstKeyword);
|
|
this->scopeContexts.back()->m_needsToComputeLexicalBlockStuffs = true;
|
|
}
|
|
}
|
|
|
|
if (nameDeclaredCallback) {
|
|
nameDeclaredCallback(name, blockIndex, kind, isExplicitVariableDeclaration);
|
|
}
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseVariableDeclaration(ASTBuilder& builder, DeclarationOptions& options, bool& hasInit, ASTNodeType& leftSideType)
|
|
{
|
|
ScannerResultVector params;
|
|
ASTNode idNode;
|
|
bool isIdentifier;
|
|
AtomicString name;
|
|
|
|
idNode = this->parsePattern(builder, params, options.kind, true);
|
|
leftSideType = idNode->type();
|
|
isIdentifier = (leftSideType == Identifier);
|
|
if (isIdentifier) {
|
|
name = idNode->asIdentifier()->name();
|
|
}
|
|
|
|
// ECMA-262 12.2.1
|
|
if (this->context->strict && isIdentifier && this->scanner->isRestrictedWord(name)) {
|
|
this->throwError(Messages::StrictVarName);
|
|
}
|
|
|
|
if (options.kind != VarKeyword && !this->context->allowLexicalDeclaration) {
|
|
this->throwError("Lexical declaration cannot appear in a single-statement context");
|
|
}
|
|
|
|
ASTNode initNode;
|
|
hasInit = false;
|
|
if (this->match(Substitution)) {
|
|
hasInit = true;
|
|
this->nextToken();
|
|
ASTNodeType type = ASTNodeType::ASTNodeTypeError;
|
|
initNode = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
|
|
if (initNode) {
|
|
type = initNode->type();
|
|
}
|
|
if (type == ASTNodeType::FunctionExpression || type == ASTNodeType::ArrowFunctionExpression) {
|
|
if (this->lastPoppedScopeContext->m_functionName == AtomicString()) {
|
|
this->lastPoppedScopeContext->m_functionName = name;
|
|
this->lastPoppedScopeContext->m_hasImplictFunctionName = true;
|
|
}
|
|
}
|
|
|
|
if (initNode->type() == ASTNodeType::ClassExpression) {
|
|
initNode->asClassExpression()->tryToSetImplicitName(name);
|
|
}
|
|
} else if (!isIdentifier && !options.inFor) {
|
|
this->expect(Substitution);
|
|
}
|
|
|
|
if (options.kind == ConstKeyword && !options.inFor) {
|
|
if (!initNode) {
|
|
this->throwError("Missing initializer in const identifier '%s' declaration", name.string());
|
|
}
|
|
}
|
|
|
|
MetaNode node = this->createNode();
|
|
return this->finalize(node, builder.createVariableDeclaratorNode(options.kind, idNode.get(), initNode.get()));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
std::tuple<bool, bool, ASTNodeVector> parseVariableDeclarationList(ASTBuilder& builder, DeclarationOptions& options)
|
|
{
|
|
DeclarationOptions opt;
|
|
opt.inFor = options.inFor;
|
|
opt.kind = options.kind;
|
|
|
|
ASTNodeVector list;
|
|
bool hasInit;
|
|
ASTNodeType leftSideType;
|
|
list.push_back(this->parseVariableDeclaration(builder, opt, hasInit, leftSideType));
|
|
while (this->match(Comma)) {
|
|
this->nextToken();
|
|
bool ignored;
|
|
ASTNodeType ignored2;
|
|
list.push_back(this->parseVariableDeclaration(builder, opt, ignored, ignored2));
|
|
}
|
|
|
|
bool leftIsArrayOrObjectPattern = (leftSideType == ArrayPattern || leftSideType == ObjectPattern);
|
|
return std::make_tuple(hasInit, leftIsArrayOrObjectPattern, list);
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseVariableStatement(ASTBuilder& builder, KeywordKind kind = VarKeyword)
|
|
{
|
|
this->expectKeyword(kind);
|
|
MetaNode node = this->createNode();
|
|
DeclarationOptions opt;
|
|
opt.inFor = false;
|
|
opt.kind = kind;
|
|
auto declarations = this->parseVariableDeclarationList(builder, opt);
|
|
ASTNodeVector declarationVector = std::get<2>(declarations);
|
|
this->consumeSemicolon();
|
|
|
|
return this->finalize(node, builder.createVariableDeclarationNode(std::move(declarationVector), kind));
|
|
}
|
|
|
|
// ECMA-262 13.4 Empty Statement
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseEmptyStatement(ASTBuilder& builder)
|
|
{
|
|
this->expect(SemiColon);
|
|
|
|
MetaNode node = this->createNode();
|
|
return this->finalize(node, builder.createEmptyStatementNode());
|
|
}
|
|
|
|
// ECMA-262 13.5 Expression Statement
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseExpressionStatement(ASTBuilder& builder)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
ASTNode expr = this->parseExpression(builder);
|
|
this->consumeSemicolon();
|
|
return this->finalize(node, builder.createExpressionStatementNode(expr.get()));
|
|
}
|
|
|
|
// ECMA-262 13.6 If statement
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseIfStatement(ASTBuilder& builder)
|
|
{
|
|
ASTNode test;
|
|
ASTNode consequent;
|
|
ASTNode alternate;
|
|
bool allowFunctionDeclaration = !this->context->strict;
|
|
|
|
this->expectKeyword(IfKeyword);
|
|
this->expect(LeftParenthesis);
|
|
|
|
test = this->parseExpression(builder);
|
|
|
|
this->expect(RightParenthesis);
|
|
|
|
bool allowLexicalDeclarationBefore = this->context->allowLexicalDeclaration;
|
|
this->context->allowLexicalDeclaration = false;
|
|
|
|
consequent = this->parseStatement(builder, allowFunctionDeclaration);
|
|
|
|
this->context->allowLexicalDeclaration = false;
|
|
if (this->matchKeyword(ElseKeyword)) {
|
|
this->nextToken();
|
|
alternate = this->parseStatement(builder, allowFunctionDeclaration);
|
|
}
|
|
|
|
this->context->allowLexicalDeclaration = allowLexicalDeclarationBefore;
|
|
|
|
return this->finalize(this->createNode(), builder.createIfStatementNode(test.get(), consequent.get(), alternate.get()));
|
|
}
|
|
|
|
// ECMA-262 13.7.2 The do-while Statement
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseDoWhileStatement(ASTBuilder& builder)
|
|
{
|
|
this->expectKeyword(DoKeyword);
|
|
|
|
bool previousInIteration = this->context->inIteration;
|
|
bool allowLexicalDeclarationBefore = this->context->allowLexicalDeclaration;
|
|
this->context->allowLexicalDeclaration = false;
|
|
this->context->inIteration = true;
|
|
|
|
ASTNode body;
|
|
body = this->parseStatement(builder, false);
|
|
this->context->inIteration = previousInIteration;
|
|
this->context->allowLexicalDeclaration = allowLexicalDeclarationBefore;
|
|
|
|
this->expectKeyword(WhileKeyword);
|
|
this->expect(LeftParenthesis);
|
|
ASTNode test;
|
|
test = this->parseExpression(builder);
|
|
|
|
this->expect(RightParenthesis);
|
|
if (this->match(SemiColon)) {
|
|
this->nextToken();
|
|
}
|
|
|
|
return this->finalize(this->createNode(), builder.createDoWhileStatementNode(test.get(), body.get()));
|
|
}
|
|
|
|
// ECMA-262 13.7.3 The while Statement
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseWhileStatement(ASTBuilder& builder)
|
|
{
|
|
bool prevInLoop = this->context->inLoop;
|
|
bool allowLexicalDeclarationBefore = this->context->allowLexicalDeclaration;
|
|
this->context->allowLexicalDeclaration = false;
|
|
this->context->inLoop = true;
|
|
|
|
this->expectKeyword(WhileKeyword);
|
|
this->expect(LeftParenthesis);
|
|
ASTNode test;
|
|
test = this->parseExpression(builder);
|
|
this->expect(RightParenthesis);
|
|
|
|
bool previousInIteration = this->context->inIteration;
|
|
this->context->inIteration = true;
|
|
ASTNode body;
|
|
body = this->parseStatement(builder, false);
|
|
this->context->inIteration = previousInIteration;
|
|
this->context->inLoop = prevInLoop;
|
|
this->context->allowLexicalDeclaration = allowLexicalDeclarationBefore;
|
|
|
|
return this->finalize(this->createNode(), builder.createWhileStatementNode(test.get(), body.get()));
|
|
}
|
|
|
|
// ECMA-262 13.7.4 The for Statement
|
|
// ECMA-262 13.7.5 The for-in and for-of Statements
|
|
|
|
enum ForStatementType {
|
|
statementTypeFor,
|
|
statementTypeForIn,
|
|
statementTypeForOf
|
|
};
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseForStatement(ASTBuilder& builder)
|
|
{
|
|
ASTNode init;
|
|
ASTNode test;
|
|
ASTNode update;
|
|
ASTNode left;
|
|
ASTNode right;
|
|
ForStatementType type = statementTypeFor;
|
|
bool prevInLoop = this->context->inLoop;
|
|
bool isLexicalDeclaration = false;
|
|
|
|
this->expectKeyword(ForKeyword);
|
|
this->expect(LeftParenthesis);
|
|
|
|
ParserBlockContext headBlockContext = openBlock();
|
|
|
|
if (this->match(SemiColon)) {
|
|
this->nextToken();
|
|
} else {
|
|
if (this->matchKeyword(VarKeyword)) {
|
|
this->nextToken();
|
|
|
|
bool previousAllowIn = this->context->allowIn;
|
|
this->context->allowIn = false;
|
|
DeclarationOptions opt;
|
|
opt.inFor = true;
|
|
opt.kind = VarKeyword;
|
|
|
|
auto declarations = this->parseVariableDeclarationList(builder, opt);
|
|
ASTNodeVector declarationVector = std::get<2>(declarations);
|
|
this->context->allowIn = previousAllowIn;
|
|
|
|
if (declarationVector.size() == 1 && this->matchKeyword(InKeyword)) {
|
|
if (std::get<0>(declarations) && (std::get<1>(declarations) || this->context->strict)) {
|
|
this->throwError(Messages::ForInOfLoopInitializer, new ASCIIString("for-in"));
|
|
}
|
|
left = this->finalize(this->createNode(), builder.createVariableDeclarationNode(std::move(declarationVector), VarKeyword));
|
|
this->nextToken();
|
|
type = statementTypeForIn;
|
|
} else if (declarationVector.size() == 1 && !std::get<0>(declarations) && this->lookahead.type == Token::IdentifierToken && this->lookahead.relatedSource() == "of") {
|
|
left = this->finalize(this->createNode(), builder.createVariableDeclarationNode(std::move(declarationVector), VarKeyword));
|
|
this->nextToken();
|
|
type = statementTypeForOf;
|
|
} else {
|
|
init = this->finalize(this->createNode(), builder.createVariableDeclarationNode(std::move(declarationVector), VarKeyword));
|
|
this->expect(SemiColon);
|
|
}
|
|
} else if (this->matchKeyword(ConstKeyword) || this->matchKeyword(LetKeyword)) {
|
|
isLexicalDeclaration = true;
|
|
const bool previousAllowLexicalDeclaration = this->context->allowLexicalDeclaration;
|
|
this->context->allowLexicalDeclaration = true;
|
|
|
|
Scanner::ScannerResult keyword = this->lookahead;
|
|
KeywordKind kind = keyword.valueKeywordKind;
|
|
this->nextToken();
|
|
|
|
if (!this->context->strict && this->matchKeyword(InKeyword)) {
|
|
this->nextToken();
|
|
left = this->finalize(this->createNode(), builder.createIdentifierNode(AtomicString(this->escargotContext, keyword.relatedSource())));
|
|
init = nullptr;
|
|
type = statementTypeForIn;
|
|
} else {
|
|
const bool previousAllowIn = this->context->allowIn;
|
|
this->context->allowIn = false;
|
|
|
|
DeclarationOptions opt;
|
|
opt.inFor = true;
|
|
opt.kind = kind;
|
|
|
|
auto declarations = this->parseVariableDeclarationList(builder, opt);
|
|
ASTNodeVector declarationVector = std::get<2>(declarations);
|
|
this->context->allowIn = previousAllowIn;
|
|
|
|
if (declarationVector.size() == 1 && !std::get<0>(declarations) && this->matchKeyword(InKeyword)) {
|
|
left = this->finalize(this->createNode(), builder.createVariableDeclarationNode(std::move(declarationVector), kind));
|
|
this->nextToken();
|
|
type = statementTypeForIn;
|
|
} else if (declarationVector.size() == 1 && !std::get<0>(declarations) && this->lookahead.type == Token::IdentifierToken && this->lookahead.relatedSource() == "of") {
|
|
left = this->finalize(this->createNode(), builder.createVariableDeclarationNode(std::move(declarationVector), kind));
|
|
this->nextToken();
|
|
type = statementTypeForOf;
|
|
} else {
|
|
init = this->finalize(this->createNode(), builder.createVariableDeclarationNode(std::move(declarationVector), kind));
|
|
this->expect(SemiColon);
|
|
}
|
|
}
|
|
this->context->allowLexicalDeclaration = previousAllowLexicalDeclaration;
|
|
} else {
|
|
ALLOC_TOKEN(initStartToken);
|
|
*initStartToken = this->lookahead;
|
|
bool previousAllowIn = this->context->allowIn;
|
|
this->context->allowIn = false;
|
|
ASTNodeType initNodeType;
|
|
init = this->inheritCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
|
|
initNodeType = init->type();
|
|
this->context->allowIn = previousAllowIn;
|
|
|
|
if (this->matchKeyword(InKeyword)) {
|
|
if (initNodeType == ASTNodeType::Literal || (initNodeType >= ASTNodeType::AssignmentExpression && initNodeType <= ASTNodeType::AssignmentExpressionSimple) || initNodeType == ASTNodeType::ThisExpression) {
|
|
this->throwError(Messages::InvalidLHSInForIn);
|
|
}
|
|
|
|
this->nextToken();
|
|
init = builder.reinterpretExpressionAsPattern(init.get());
|
|
left = init;
|
|
init = nullptr;
|
|
type = statementTypeForIn;
|
|
} else if (this->lookahead.type == Token::IdentifierToken && this->lookahead.relatedSource() == "of") {
|
|
if (!this->context->isAssignmentTarget || (initNodeType >= ASTNodeType::AssignmentExpression && initNodeType <= ASTNodeType::AssignmentExpressionSimple)) {
|
|
this->throwError(Messages::InvalidLHSInForLoop);
|
|
}
|
|
|
|
this->nextToken();
|
|
init = builder.reinterpretExpressionAsPattern(init.get());
|
|
left = init;
|
|
init = nullptr;
|
|
type = statementTypeForOf;
|
|
} else {
|
|
if (this->match(Comma)) {
|
|
ASTNodeVector initSeq;
|
|
initSeq.push_back(init);
|
|
while (this->match(Comma)) {
|
|
this->nextToken();
|
|
initSeq.push_back(this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<ASTBuilder, false>));
|
|
}
|
|
init = this->finalize(this->startNode(initStartToken), builder.createSequenceExpressionNode(std::move(initSeq)));
|
|
}
|
|
this->expect(SemiColon);
|
|
}
|
|
}
|
|
}
|
|
|
|
ParserBlockContext iterationBlockContext;
|
|
|
|
if (type == statementTypeFor) {
|
|
iterationBlockContext = openBlock();
|
|
this->context->inLoop = true;
|
|
if (!this->match(SemiColon)) {
|
|
test = this->parseExpression(builder);
|
|
}
|
|
this->expect(SemiColon);
|
|
if (!this->match(RightParenthesis)) {
|
|
update = this->parseExpression(builder);
|
|
}
|
|
} else if (type == statementTypeForIn) {
|
|
ASSERT(left);
|
|
right = this->parseExpression(builder);
|
|
iterationBlockContext = openBlock();
|
|
} else {
|
|
ASSERT(type == statementTypeForOf);
|
|
ASSERT(left);
|
|
right = this->parseAssignmentExpression<ASTBuilder, false>(builder);
|
|
iterationBlockContext = openBlock();
|
|
}
|
|
|
|
this->expect(RightParenthesis);
|
|
|
|
ASTBlockScopeContext* headBlockContextInstance = this->scopeContexts.back()->findBlockFromBackward(headBlockContext.childLexicalBlockIndex);
|
|
if (headBlockContextInstance->m_names.size()) {
|
|
// if there are names on headContext (let, const)
|
|
// we should copy names into to iterationBlock
|
|
this->scopeContexts.back()->findBlockFromBackward(iterationBlockContext.childLexicalBlockIndex)->m_names = headBlockContextInstance->m_names;
|
|
}
|
|
|
|
bool previousInIteration = this->context->inIteration;
|
|
bool allowLexicalDeclarationBefore = this->context->allowLexicalDeclaration;
|
|
this->context->allowLexicalDeclaration = false;
|
|
this->context->inIteration = true;
|
|
ASTNode body;
|
|
auto functor = std::bind(&Parser::parseStatement<ASTBuilder>, std::ref(*this), builder, false);
|
|
body = this->isolateCoverGrammarWithFunctor(builder, functor);
|
|
|
|
this->context->inIteration = previousInIteration;
|
|
this->context->inLoop = prevInLoop;
|
|
this->context->allowLexicalDeclaration = allowLexicalDeclarationBefore;
|
|
|
|
closeBlock(iterationBlockContext);
|
|
closeBlock(headBlockContext);
|
|
|
|
MetaNode node = this->createNode();
|
|
|
|
if (type == statementTypeFor) {
|
|
ASTNodePtr forNode = builder.createForStatementNode(init.get(), test.get(), update.get(), body.get(), isLexicalDeclaration, headBlockContext.childLexicalBlockIndex, iterationBlockContext.childLexicalBlockIndex);
|
|
return this->finalize(node, forNode);
|
|
}
|
|
|
|
if (type == statementTypeForIn) {
|
|
ASTNodePtr forInNode = builder.createForInOfStatementNode(left.get(), right.get(), body.get(), true, isLexicalDeclaration, headBlockContext.childLexicalBlockIndex, iterationBlockContext.childLexicalBlockIndex);
|
|
return this->finalize(node, forInNode);
|
|
}
|
|
|
|
ASSERT(type == statementTypeForOf);
|
|
ASTNodePtr forOfNode = builder.createForInOfStatementNode(left.get(), right.get(), body.get(), false, isLexicalDeclaration, headBlockContext.childLexicalBlockIndex, iterationBlockContext.childLexicalBlockIndex);
|
|
return this->finalize(node, forOfNode);
|
|
}
|
|
|
|
void removeLabel(AtomicString label)
|
|
{
|
|
for (size_t i = 0; i < this->context->labelSet.size(); i++) {
|
|
if (this->context->labelSet[i].first == label) {
|
|
this->context->labelSet.erase(this->context->labelSet.begin() + i);
|
|
return;
|
|
}
|
|
}
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
bool hasLabel(AtomicString label)
|
|
{
|
|
for (size_t i = 0; i < this->context->labelSet.size(); i++) {
|
|
if (this->context->labelSet[i].first == label) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
// ECMA-262 13.8 The continue statement
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseContinueStatement(ASTBuilder& builder)
|
|
{
|
|
this->expectKeyword(ContinueKeyword);
|
|
|
|
AtomicString labelString;
|
|
if (this->lookahead.type == IdentifierToken && !this->hasLineTerminator) {
|
|
ASTNode labelNode = this->parseVariableIdentifier(builder);
|
|
labelString = labelNode->asIdentifier()->name();
|
|
|
|
if (!hasLabel(labelString)) {
|
|
this->throwError(Messages::UnknownLabel, labelString.string());
|
|
}
|
|
|
|
for (size_t i = 0; i < this->context->labelSet.size(); i++) {
|
|
if (this->context->labelSet[i].first == labelString && !this->context->labelSet[i].second) {
|
|
this->throwError(Messages::UnknownLabel, labelString.string());
|
|
}
|
|
}
|
|
} else if (!this->context->inIteration) {
|
|
this->throwError(Messages::IllegalContinue);
|
|
}
|
|
|
|
this->consumeSemicolon();
|
|
|
|
MetaNode node = this->createNode();
|
|
if (labelString.string()->length() != 0) {
|
|
return this->finalize(node, builder.createContinueLabelStatementNode(labelString.string()));
|
|
} else {
|
|
return this->finalize(node, builder.createContinueStatementNode());
|
|
}
|
|
}
|
|
|
|
// ECMA-262 13.9 The break statement
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseBreakStatement(ASTBuilder& builder)
|
|
{
|
|
this->expectKeyword(BreakKeyword);
|
|
|
|
AtomicString labelString;
|
|
if (this->lookahead.type == IdentifierToken && !this->hasLineTerminator) {
|
|
ASTNode labelNode = this->parseVariableIdentifier(builder);
|
|
labelString = labelNode->asIdentifier()->name();
|
|
|
|
if (!hasLabel(labelString)) {
|
|
this->throwError(Messages::UnknownLabel, labelString.string());
|
|
}
|
|
} else if (!this->context->inIteration && !this->context->inSwitch) {
|
|
this->throwError(Messages::IllegalBreak);
|
|
}
|
|
|
|
this->consumeSemicolon();
|
|
|
|
MetaNode node = this->createNode();
|
|
if (labelString.string()->length() != 0) {
|
|
return this->finalize(node, builder.createBreakLabelStatementNode(labelString.string()));
|
|
} else {
|
|
return this->finalize(node, builder.createBreakStatementNode());
|
|
}
|
|
}
|
|
|
|
// ECMA-262 13.10 The return statement
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseReturnStatement(ASTBuilder& builder)
|
|
{
|
|
if (!this->context->inFunctionBody) {
|
|
this->throwError(Messages::IllegalReturn);
|
|
}
|
|
|
|
this->expectKeyword(ReturnKeyword);
|
|
|
|
bool hasArgument = !this->match(SemiColon) && !this->match(RightBrace) && !this->hasLineTerminator && this->lookahead.type != EOFToken;
|
|
ASTNode argument;
|
|
if (hasArgument) {
|
|
argument = this->parseExpression(builder);
|
|
}
|
|
this->consumeSemicolon();
|
|
|
|
return this->finalize(this->createNode(), builder.createReturnStatementNode(argument.get()));
|
|
}
|
|
|
|
// ECMA-262 13.11 The with statement
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseWithStatement(ASTBuilder& builder)
|
|
{
|
|
if (this->context->strict) {
|
|
this->throwError(Messages::StrictModeWith);
|
|
}
|
|
|
|
this->expectKeyword(WithKeyword);
|
|
MetaNode node = this->createNode();
|
|
this->expect(LeftParenthesis);
|
|
ASTNode object = this->parseExpression(builder);
|
|
this->expect(RightParenthesis);
|
|
|
|
scopeContexts.back()->m_hasWith = true;
|
|
|
|
bool prevInWith = this->context->inWith;
|
|
this->context->inWith = true;
|
|
|
|
ASTNode body = this->parseStatement(builder, false);
|
|
this->context->inWith = prevInWith;
|
|
|
|
return this->finalize(node, builder.createWithStatementNode(object, body.get()));
|
|
}
|
|
|
|
// ECMA-262 13.12 The switch statement
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseSwitchCase(ASTBuilder& builder, bool& isDefaultNode)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
ASTNode test;
|
|
if (this->matchKeyword(DefaultKeyword)) {
|
|
node = this->createNode();
|
|
this->nextToken();
|
|
isDefaultNode = true;
|
|
} else {
|
|
this->expectKeyword(CaseKeyword);
|
|
node = this->createNode();
|
|
test = this->parseExpression(builder);
|
|
isDefaultNode = false;
|
|
}
|
|
this->expect(Colon);
|
|
|
|
ASTStatementContainer consequent = builder.createStatementContainer();
|
|
while (true) {
|
|
if (this->match(RightBrace) || this->matchKeyword(DefaultKeyword) || this->matchKeyword(CaseKeyword)) {
|
|
break;
|
|
}
|
|
consequent->appendChild(this->parseStatementListItem(builder).get());
|
|
}
|
|
|
|
return this->finalize(node, builder.createSwitchCaseNode(test.get(), consequent.get()));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseSwitchStatement(ASTBuilder& builder)
|
|
{
|
|
this->expectKeyword(SwitchKeyword);
|
|
|
|
this->expect(LeftParenthesis);
|
|
ASTNode discriminant;
|
|
discriminant = this->parseExpression(builder);
|
|
this->expect(RightParenthesis);
|
|
|
|
bool previousInSwitch = this->context->inSwitch;
|
|
this->context->inSwitch = true;
|
|
|
|
ASTStatementContainer casesA, casesB;
|
|
ASTNode deflt;
|
|
casesA = builder.createStatementContainer();
|
|
casesB = builder.createStatementContainer();
|
|
|
|
bool isDefaultNode = false;
|
|
bool defaultFound = false;
|
|
this->expect(LeftBrace);
|
|
|
|
while (true) {
|
|
if (this->match(RightBrace)) {
|
|
break;
|
|
}
|
|
|
|
ASTNode clause = this->parseSwitchCase(builder, isDefaultNode);
|
|
if (isDefaultNode) {
|
|
if (defaultFound) {
|
|
this->throwError(Messages::MultipleDefaultsInSwitch);
|
|
}
|
|
deflt = clause;
|
|
defaultFound = true;
|
|
} else {
|
|
if (defaultFound) {
|
|
casesA->appendChild(clause.get());
|
|
} else {
|
|
casesB->appendChild(clause.get());
|
|
}
|
|
}
|
|
}
|
|
this->expect(RightBrace);
|
|
|
|
this->context->inSwitch = previousInSwitch;
|
|
return this->finalize(this->createNode(), builder.createSwitchStatementNode(discriminant.get(), casesA.get(), deflt.get(), casesB.get()));
|
|
}
|
|
|
|
// ECMA-262 13.13 Labelled Statements
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseLabelledStatement(ASTBuilder& builder, size_t multiLabelCount = 1)
|
|
{
|
|
ASTNode expr;
|
|
AtomicString name;
|
|
bool isIdentifier = false;
|
|
|
|
expr = this->parseExpression(builder);
|
|
if (expr->type() == Identifier) {
|
|
isIdentifier = true;
|
|
name = expr->asIdentifier()->name();
|
|
}
|
|
|
|
ASTNodePtr statement;
|
|
if (isIdentifier && this->match(Colon)) {
|
|
this->nextToken();
|
|
|
|
if (hasLabel(name)) {
|
|
this->throwError(Messages::Redeclaration, new ASCIIString("Label"), name.string());
|
|
}
|
|
|
|
this->context->labelSet.push_back(std::make_pair(name, false));
|
|
|
|
if (this->lookahead.type == IdentifierToken) {
|
|
ASTNode labeledBody = this->parseLabelledStatement(builder, multiLabelCount + 1);
|
|
statement = builder.createLabeledStatementNode(labeledBody.get(), name.string());
|
|
} else {
|
|
if (this->matchKeyword(DoKeyword) || this->matchKeyword(ForKeyword) || this->matchKeyword(WhileKeyword)) {
|
|
// Turn labels to accept continue references.
|
|
size_t end = this->context->labelSet.size() - 1;
|
|
|
|
for (size_t i = 0; i < multiLabelCount; i++) {
|
|
ASSERT(end >= i);
|
|
this->context->labelSet[end - i].second = true;
|
|
}
|
|
}
|
|
|
|
ASTNode labeledBody = this->parseStatement(builder, !this->context->strict);
|
|
statement = builder.createLabeledStatementNode(labeledBody.get(), name.string());
|
|
}
|
|
removeLabel(name);
|
|
} else {
|
|
this->consumeSemicolon();
|
|
statement = builder.createExpressionStatementNode(expr.get());
|
|
}
|
|
|
|
return this->finalize(this->createNode(), statement);
|
|
}
|
|
|
|
// ECMA-262 13.14 The throw statement
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseThrowStatement(ASTBuilder& builder)
|
|
{
|
|
auto metaNode = this->createNode();
|
|
this->expectKeyword(ThrowKeyword);
|
|
|
|
if (this->hasLineTerminator) {
|
|
this->throwError(Messages::NewlineAfterThrow);
|
|
}
|
|
|
|
ASTNode argument;
|
|
argument = this->parseExpression(builder);
|
|
this->consumeSemicolon();
|
|
|
|
return this->finalize(metaNode, builder.createThrowStatementNode(argument.get()));
|
|
}
|
|
|
|
// ECMA-262 13.15 The try statement
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseCatchClause(ASTBuilder& builder)
|
|
{
|
|
this->expectKeyword(CatchKeyword);
|
|
|
|
this->expect(LeftParenthesis);
|
|
if (this->match(RightParenthesis)) {
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
|
|
ParserBlockContext catchBlockContext = openBlock();
|
|
|
|
ScannerResultVector params;
|
|
ASTNode param = this->parsePattern(builder, params, KeywordKind::LetKeyword);
|
|
|
|
if (this->context->strict && param->type() == Identifier) {
|
|
if (this->scanner->isRestrictedWord(param->asIdentifier()->name())) {
|
|
this->throwError(Messages::StrictCatchVariable);
|
|
}
|
|
}
|
|
|
|
this->expect(RightParenthesis);
|
|
|
|
bool oldInCatchClause = this->context->inCatchClause;
|
|
this->context->inCatchClause = true;
|
|
|
|
bool gotSimplyDeclaredVariableName = param->isIdentifier();
|
|
|
|
if (gotSimplyDeclaredVariableName) {
|
|
this->context->catchClauseSimplyDeclaredVariableNames.push_back(param->asIdentifier()->name());
|
|
}
|
|
|
|
ASTNode body;
|
|
body = this->parseBlock(builder);
|
|
|
|
if (gotSimplyDeclaredVariableName) {
|
|
this->context->catchClauseSimplyDeclaredVariableNames.pop_back();
|
|
}
|
|
|
|
this->context->inCatchClause = oldInCatchClause;
|
|
|
|
closeBlock(catchBlockContext);
|
|
|
|
return this->finalize(this->createNode(), builder.createCatchClauseNode(param.get(), nullptr, body.get(), catchBlockContext.childLexicalBlockIndex));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseFinallyClause(ASTBuilder& builder)
|
|
{
|
|
this->expectKeyword(FinallyKeyword);
|
|
return this->parseBlock(builder);
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseTryStatement(ASTBuilder& builder)
|
|
{
|
|
this->expectKeyword(TryKeyword);
|
|
|
|
ASTNode block = this->parseBlock(builder);
|
|
ASTNode handler;
|
|
if (this->matchKeyword(CatchKeyword)) {
|
|
handler = this->parseCatchClause(builder);
|
|
}
|
|
ASTNode finalizer;
|
|
if (this->matchKeyword(FinallyKeyword)) {
|
|
finalizer = this->parseFinallyClause(builder);
|
|
}
|
|
|
|
if (!handler && !finalizer) {
|
|
this->throwError(Messages::NoCatchOrFinally);
|
|
}
|
|
|
|
return this->finalize(this->createNode(), builder.createTryStatementNode(block.get(), handler.get(), finalizer.get()));
|
|
}
|
|
|
|
// ECMA-262 13.16 The debugger statement
|
|
template <class ASTBuilder>
|
|
ASTNode parseDebuggerStatement(ASTBuilder& builder)
|
|
{
|
|
ESCARGOT_LOG_ERROR("debugger keyword is not supported yet");
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(KeywordKind::DebuggerKeyword);
|
|
this->consumeSemicolon();
|
|
return this->finalize(node, builder.createDebuggerStatementNode());
|
|
}
|
|
|
|
// ECMA-262 13 Statements
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseStatement(ASTBuilder& builder, bool allowFunctionDeclaration = true)
|
|
{
|
|
checkRecursiveLimit();
|
|
ASTNode statement;
|
|
switch (this->lookahead.type) {
|
|
case Token::BooleanLiteralToken:
|
|
case Token::NullLiteralToken:
|
|
case Token::NumericLiteralToken:
|
|
case Token::StringLiteralToken:
|
|
case Token::TemplateToken:
|
|
case Token::RegularExpressionToken:
|
|
statement = this->parseExpressionStatement(builder);
|
|
break;
|
|
|
|
case Token::PunctuatorToken: {
|
|
PunctuatorKind value = this->lookahead.valuePunctuatorKind;
|
|
if (value == LeftBrace) {
|
|
statement = this->parseBlock(builder);
|
|
} else if (value == LeftParenthesis) {
|
|
statement = this->parseExpressionStatement(builder);
|
|
} else if (value == SemiColon) {
|
|
statement = this->parseEmptyStatement(builder);
|
|
} else {
|
|
statement = this->parseExpressionStatement(builder);
|
|
}
|
|
break;
|
|
}
|
|
case Token::IdentifierToken:
|
|
statement = this->parseLabelledStatement(builder);
|
|
break;
|
|
|
|
case Token::KeywordToken:
|
|
switch (this->lookahead.valueKeywordKind) {
|
|
case BreakKeyword:
|
|
statement = this->parseBreakStatement(builder);
|
|
break;
|
|
case ContinueKeyword:
|
|
statement = this->parseContinueStatement(builder);
|
|
break;
|
|
case DebuggerKeyword:
|
|
statement = this->parseDebuggerStatement(builder);
|
|
break;
|
|
case DoKeyword:
|
|
statement = this->parseDoWhileStatement(builder);
|
|
break;
|
|
case ForKeyword:
|
|
statement = this->parseForStatement(builder);
|
|
break;
|
|
case FunctionKeyword: {
|
|
if (!allowFunctionDeclaration) {
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
statement = this->parseFunctionDeclaration(builder);
|
|
break;
|
|
}
|
|
case IfKeyword:
|
|
statement = this->parseIfStatement(builder);
|
|
break;
|
|
case ReturnKeyword:
|
|
statement = this->parseReturnStatement(builder);
|
|
break;
|
|
case SwitchKeyword:
|
|
statement = this->parseSwitchStatement(builder);
|
|
break;
|
|
case ThrowKeyword:
|
|
statement = this->parseThrowStatement(builder);
|
|
break;
|
|
case TryKeyword:
|
|
statement = this->parseTryStatement(builder);
|
|
break;
|
|
// Lexical declaration (let, const) cannot appear in a single-statement context
|
|
case VarKeyword:
|
|
statement = this->parseVariableStatement(builder, this->lookahead.valueKeywordKind);
|
|
break;
|
|
case WhileKeyword:
|
|
statement = this->parseWhileStatement(builder);
|
|
break;
|
|
case WithKeyword:
|
|
statement = this->parseWithStatement(builder);
|
|
break;
|
|
case ClassKeyword:
|
|
statement = this->parseClassDeclaration(builder);
|
|
break;
|
|
case YieldKeyword: {
|
|
// TODO consider case that class method is generator function
|
|
if (builder.isNodeGenerator()) {
|
|
if (this->context->strict) {
|
|
this->throwError("Cannot use yield as a label in strict mode");
|
|
}
|
|
statement = this->parseLabelledStatement(builder);
|
|
} else {
|
|
statement = this->parseExpressionStatement(builder);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
statement = this->parseExpressionStatement(builder);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
|
|
return statement;
|
|
}
|
|
|
|
PassRefPtr<StatementContainer> parseFunctionParameters()
|
|
{
|
|
RefPtr<StatementContainer> container = StatementContainer::create();
|
|
|
|
this->expect(LeftParenthesis);
|
|
ParseFormalParametersResult options;
|
|
size_t paramIndex = 0;
|
|
MetaNode node = this->createNode();
|
|
while (!this->match(RightParenthesis)) {
|
|
bool end = !this->parseFormalParameter(options);
|
|
|
|
RefPtr<Node> param = options.params.back();
|
|
|
|
switch (param->type()) {
|
|
case Identifier:
|
|
case AssignmentPattern:
|
|
case ArrayPattern:
|
|
case ObjectPattern: {
|
|
RefPtr<InitializeParameterExpressionNode> init = this->finalize(node, new InitializeParameterExpressionNode(param.get(), paramIndex));
|
|
RefPtr<Node> statement = this->finalize(node, new ExpressionStatementNode(init.get()));
|
|
container->appendChild(statement->asStatement());
|
|
break;
|
|
}
|
|
case RestElement: {
|
|
RefPtr<Node> statement = this->finalize(node, new ExpressionStatementNode(param.get()));
|
|
container->appendChild(statement->asStatement());
|
|
break;
|
|
}
|
|
default: {
|
|
ASSERT_NOT_REACHED();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (end) {
|
|
break;
|
|
}
|
|
paramIndex++;
|
|
this->expect(Comma);
|
|
}
|
|
this->expect(RightParenthesis);
|
|
|
|
return container;
|
|
}
|
|
|
|
|
|
PassRefPtr<BlockStatementNode> parseFunctionBody(NodeGenerator& builder)
|
|
{
|
|
// only for parsing the body of callee function
|
|
ASSERT(this->isParsingSingleFunction);
|
|
|
|
this->lexicalBlockIndex = 0;
|
|
this->lexicalBlockCount = 0;
|
|
|
|
this->subCodeBlockIndex = 0;
|
|
|
|
this->context->inCatchClause = false;
|
|
|
|
MetaNode nodeStart = this->createNode();
|
|
RefPtr<StatementContainer> body = StatementContainer::create();
|
|
|
|
this->expect(LeftBrace);
|
|
this->parseDirectivePrologues(body.get());
|
|
|
|
this->context->labelSet.clear();
|
|
this->context->inIteration = false;
|
|
this->context->inSwitch = false;
|
|
this->context->inFunctionBody = true;
|
|
|
|
Node* referNode = nullptr;
|
|
while (this->startMarker.index < this->scanner->length) {
|
|
if (this->match(RightBrace)) {
|
|
break;
|
|
}
|
|
referNode = body->appendChild(this->parseStatementListItem(builder).get(), referNode);
|
|
}
|
|
|
|
bool isEndedWithReturnNode = referNode && referNode->type() == ASTNodeType::ReturnStatement;
|
|
if (!isEndedWithReturnNode) {
|
|
referNode = body->appendChild(adoptRef(builder.createReturnStatementNode(nullptr)));
|
|
}
|
|
|
|
this->expect(RightBrace);
|
|
|
|
return this->finalize(nodeStart, builder.createBlockStatementNode(body.get(), 0));
|
|
}
|
|
|
|
// ECMA-262 14.1 Function Definition
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseFunctionSourceElements(ASTBuilder& builder)
|
|
{
|
|
// return empty node because the function body node is never used now
|
|
ASTNode result;
|
|
if (this->isParsingSingleFunction) {
|
|
// when parsing for function call,
|
|
// parseFunctionSourceElements should parse only for child functions
|
|
ASSERT(this->childFunctionIndex > 0);
|
|
|
|
size_t realIndex = this->childFunctionIndex - 1;
|
|
this->childFunctionIndex++;
|
|
InterpretedCodeBlock* currentTarget = this->codeBlock->asInterpretedCodeBlock();
|
|
size_t orgIndex = this->lookahead.start;
|
|
this->expect(LeftBrace);
|
|
|
|
StringView src = currentTarget->childBlocks()[realIndex]->bodySrc();
|
|
this->scanner->index = src.length() + orgIndex;
|
|
|
|
this->scanner->lineNumber = currentTarget->childBlocks()[realIndex]->sourceElementStart().line;
|
|
this->scanner->lineStart = currentTarget->childBlocks()[realIndex]->sourceElementStart().index - currentTarget->childBlocks()[realIndex]->sourceElementStart().column;
|
|
this->lookahead.lineNumber = this->scanner->lineNumber;
|
|
this->lookahead.lineStart = this->scanner->lineStart;
|
|
this->lookahead.type = Token::PunctuatorToken;
|
|
this->lookahead.valuePunctuatorKind = PunctuatorKind::RightBrace;
|
|
this->expect(RightBrace);
|
|
|
|
//return this->finalize(this->createNode(), builder.createBlockStatementNode(StatementContainer::create().get(), this->lexicalBlockIndex));
|
|
return result;
|
|
}
|
|
|
|
ASSERT(!this->isParsingSingleFunction);
|
|
|
|
LexicalBlockIndex lexicalBlockIndexBefore = this->lexicalBlockIndex;
|
|
LexicalBlockIndex lexicalBlockCountBefore = this->lexicalBlockCount;
|
|
auto oldNameCallback = this->nameDeclaredCallback;
|
|
|
|
this->lexicalBlockIndex = 0;
|
|
this->lexicalBlockCount = 0;
|
|
|
|
size_t oldSubCodeBlockIndex = this->subCodeBlockIndex;
|
|
this->subCodeBlockIndex = 0;
|
|
|
|
bool oldInCatchClause = this->context->inCatchClause;
|
|
this->context->inCatchClause = false;
|
|
|
|
auto oldCatchClauseSimplyDeclaredVariableNames = std::move(this->context->catchClauseSimplyDeclaredVariableNames);
|
|
|
|
MetaNode nodeStart = this->createNode();
|
|
RefPtr<StatementContainer> body = StatementContainer::create();
|
|
|
|
this->expect(LeftBrace);
|
|
this->parseDirectivePrologues(body.get());
|
|
|
|
auto previousLabelSet = this->context->labelSet;
|
|
bool previousInIteration = this->context->inIteration;
|
|
bool previousInSwitch = this->context->inSwitch;
|
|
bool previousInFunctionBody = this->context->inFunctionBody;
|
|
|
|
this->context->labelSet.clear();
|
|
this->context->inIteration = false;
|
|
this->context->inSwitch = false;
|
|
this->context->inFunctionBody = true;
|
|
|
|
// parseFunctionSourceElements only scans the function body
|
|
SyntaxChecker newBuilder;
|
|
while (this->startMarker.index < this->scanner->length) {
|
|
if (this->match(RightBrace)) {
|
|
break;
|
|
}
|
|
this->parseStatementListItem(newBuilder);
|
|
}
|
|
|
|
this->expect(RightBrace);
|
|
|
|
this->context->inCatchClause = oldInCatchClause;
|
|
this->context->catchClauseSimplyDeclaredVariableNames = std::move(oldCatchClauseSimplyDeclaredVariableNames);
|
|
|
|
this->lexicalBlockIndex = lexicalBlockIndexBefore;
|
|
this->lexicalBlockCount = lexicalBlockCountBefore;
|
|
this->nameDeclaredCallback = oldNameCallback;
|
|
|
|
this->subCodeBlockIndex = oldSubCodeBlockIndex;
|
|
|
|
this->context->labelSet = previousLabelSet;
|
|
this->context->inIteration = previousInIteration;
|
|
this->context->inSwitch = previousInSwitch;
|
|
this->context->inFunctionBody = previousInFunctionBody;
|
|
|
|
scopeContexts.back()->m_lexicalBlockIndexFunctionLocatedIn = lexicalBlockIndexBefore;
|
|
scopeContexts.back()->m_bodyStartLOC.line = nodeStart.line;
|
|
scopeContexts.back()->m_bodyStartLOC.column = nodeStart.column;
|
|
scopeContexts.back()->m_bodyStartLOC.index = nodeStart.index;
|
|
|
|
scopeContexts.back()->m_bodyEndLOC.index = this->lastMarker.index;
|
|
#ifndef NDEBUG
|
|
scopeContexts.back()->m_bodyEndLOC.line = this->lastMarker.lineNumber;
|
|
scopeContexts.back()->m_bodyEndLOC.column = this->lastMarker.index - this->lastMarker.lineStart;
|
|
#endif
|
|
|
|
//return this->finalize(nodeStart, builder.createBlockStatementNode(StatementContainer::create().get(), this->lexicalBlockIndex));
|
|
return result;
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseFunctionDeclaration(ASTBuilder& builder)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(FunctionKeyword);
|
|
|
|
bool isGenerator = this->match(Multiply);
|
|
if (isGenerator) {
|
|
this->nextToken();
|
|
}
|
|
|
|
const char* message = nullptr;
|
|
ASTNode id;
|
|
Scanner::ScannerResult firstRestricted;
|
|
|
|
{
|
|
ALLOC_TOKEN(token);
|
|
*token = this->lookahead;
|
|
id = this->parseVariableIdentifier(builder);
|
|
|
|
if (this->context->strict) {
|
|
if (this->scanner->isRestrictedWord(token->relatedSource())) {
|
|
this->throwUnexpectedToken(token, Messages::StrictFunctionName);
|
|
}
|
|
} else {
|
|
if (this->scanner->isRestrictedWord(token->relatedSource())) {
|
|
firstRestricted = *token;
|
|
message = Messages::StrictFunctionName;
|
|
} else if (this->scanner->isStrictModeReservedWord(token->relatedSource())) {
|
|
firstRestricted = *token;
|
|
message = Messages::StrictReservedWord;
|
|
}
|
|
}
|
|
}
|
|
|
|
MetaNode paramsStart = this->createNode();
|
|
this->expect(LeftParenthesis);
|
|
AtomicString fnName = id ? id->asIdentifier()->name() : AtomicString();
|
|
|
|
ASSERT(id);
|
|
addDeclaredNameIntoContext(fnName, this->lexicalBlockIndex, KeywordKind::VarKeyword);
|
|
insertUsingName(fnName);
|
|
pushScopeContext(fnName);
|
|
|
|
scopeContexts.back()->m_paramsStartLOC.index = paramsStart.index;
|
|
scopeContexts.back()->m_paramsStartLOC.column = paramsStart.column;
|
|
scopeContexts.back()->m_paramsStartLOC.line = paramsStart.line;
|
|
|
|
bool previousAllowYield = this->context->allowYield;
|
|
bool previousInArrowFunction = this->context->inArrowFunction;
|
|
this->context->allowYield = !isGenerator;
|
|
this->context->inArrowFunction = false;
|
|
|
|
ParseFormalParametersResult formalParameters = this->parseFormalParameters(&firstRestricted);
|
|
Scanner::ScannerResult stricted = formalParameters.stricted;
|
|
firstRestricted = formalParameters.firstRestricted;
|
|
if (formalParameters.message) {
|
|
message = formalParameters.message;
|
|
}
|
|
|
|
extractNamesFromFunctionParams(formalParameters);
|
|
bool previousStrict = this->context->strict;
|
|
ASTNode body = this->parseFunctionSourceElements(builder);
|
|
if (this->context->strict && firstRestricted) {
|
|
this->throwUnexpectedToken(&firstRestricted, message);
|
|
}
|
|
if (this->context->strict && stricted) {
|
|
this->throwUnexpectedToken(&stricted, message);
|
|
}
|
|
this->context->strict = previousStrict;
|
|
this->context->allowYield = previousAllowYield;
|
|
this->context->inArrowFunction = previousInArrowFunction;
|
|
|
|
scopeContexts.back()->m_nodeType = ASTNodeType::FunctionDeclaration;
|
|
scopeContexts.back()->m_isGenerator = isGenerator;
|
|
return this->finalize(node, builder.createFunctionDeclarationNode(popScopeContext(node), isGenerator, subCodeBlockIndex));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseFunctionExpression(ASTBuilder& builder)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(FunctionKeyword);
|
|
|
|
bool isGenerator = this->match(Multiply);
|
|
if (isGenerator) {
|
|
this->nextToken();
|
|
}
|
|
|
|
const char* message = nullptr;
|
|
ASTNode id;
|
|
Scanner::ScannerResult firstRestricted;
|
|
|
|
bool previousAllowYield = this->context->allowYield;
|
|
bool previousInArrowFunction = this->context->inArrowFunction;
|
|
this->context->allowYield = !isGenerator;
|
|
this->context->inArrowFunction = false;
|
|
|
|
if (!this->match(LeftParenthesis)) {
|
|
ALLOC_TOKEN(token);
|
|
*token = this->lookahead;
|
|
id = (!this->context->strict && !isGenerator && this->matchKeyword(YieldKeyword)) ? this->parseIdentifierName(builder) : this->parseVariableIdentifier(builder);
|
|
|
|
if (this->context->strict) {
|
|
if (this->scanner->isRestrictedWord(token->relatedSource())) {
|
|
this->throwUnexpectedToken(token, Messages::StrictFunctionName);
|
|
}
|
|
} else {
|
|
if (this->scanner->isRestrictedWord(token->relatedSource())) {
|
|
firstRestricted = *token;
|
|
message = Messages::StrictFunctionName;
|
|
} else if (this->scanner->isStrictModeReservedWord(token->relatedSource())) {
|
|
firstRestricted = *token;
|
|
message = Messages::StrictReservedWord;
|
|
}
|
|
}
|
|
}
|
|
|
|
MetaNode paramsStart = this->createNode();
|
|
this->expect(LeftParenthesis);
|
|
AtomicString fnName = id ? id->asIdentifier()->name() : AtomicString();
|
|
|
|
pushScopeContext(fnName);
|
|
if (id) {
|
|
scopeContexts.back()->insertVarName(fnName, 0, false);
|
|
scopeContexts.back()->insertUsingName(fnName, 0);
|
|
}
|
|
|
|
scopeContexts.back()->m_paramsStartLOC.index = paramsStart.index;
|
|
scopeContexts.back()->m_paramsStartLOC.column = paramsStart.column;
|
|
scopeContexts.back()->m_paramsStartLOC.line = paramsStart.line;
|
|
|
|
ParseFormalParametersResult formalParameters = this->parseFormalParameters(&firstRestricted);
|
|
Scanner::ScannerResult stricted = formalParameters.stricted;
|
|
firstRestricted = formalParameters.firstRestricted;
|
|
if (formalParameters.message) {
|
|
message = formalParameters.message;
|
|
}
|
|
|
|
extractNamesFromFunctionParams(formalParameters);
|
|
bool previousStrict = this->context->strict;
|
|
ASTNode body = this->parseFunctionSourceElements(builder);
|
|
if (this->context->strict && firstRestricted) {
|
|
this->throwUnexpectedToken(&firstRestricted, message);
|
|
}
|
|
if (this->context->strict && stricted) {
|
|
this->throwUnexpectedToken(&stricted, message);
|
|
}
|
|
this->context->strict = previousStrict;
|
|
this->context->allowYield = previousAllowYield;
|
|
this->context->inArrowFunction = previousInArrowFunction;
|
|
|
|
scopeContexts.back()->m_nodeType = ASTNodeType::FunctionExpression;
|
|
scopeContexts.back()->m_isGenerator = isGenerator;
|
|
return this->finalize(node, builder.createFunctionExpressionNode(popScopeContext(node), isGenerator, subCodeBlockIndex));
|
|
}
|
|
|
|
// ECMA-262 14.1.1 Directive Prologues
|
|
// FIXME parse Directive by NodeGenerator only
|
|
|
|
PassRefPtr<Node> parseDirective()
|
|
{
|
|
ALLOC_TOKEN(token);
|
|
*token = this->lookahead;
|
|
StringView directiveValue;
|
|
bool isLiteral = false;
|
|
|
|
NodeGenerator newBuilder;
|
|
MetaNode node = this->createNode();
|
|
RefPtr<Node> expr = this->parseExpression(newBuilder);
|
|
if (expr->type() == Literal) {
|
|
isLiteral = true;
|
|
directiveValue = token->valueStringLiteral();
|
|
}
|
|
this->consumeSemicolon();
|
|
|
|
if (isLiteral) {
|
|
return this->finalize(node, newBuilder.createDirectiveNode(expr.get(), directiveValue));
|
|
} else {
|
|
return this->finalize(node, newBuilder.createExpressionStatementNode(expr.get()));
|
|
}
|
|
}
|
|
|
|
void parseDirectivePrologues(StatementContainer* container)
|
|
{
|
|
ASSERT(container != nullptr);
|
|
|
|
Scanner::ScannerResult firstRestricted;
|
|
|
|
ALLOC_TOKEN(token);
|
|
while (true) {
|
|
*token = this->lookahead;
|
|
if (token->type != StringLiteralToken) {
|
|
break;
|
|
}
|
|
|
|
RefPtr<Node> statement = this->parseDirective();
|
|
container->appendChild(statement->asStatement());
|
|
|
|
if (statement->type() != Directive) {
|
|
break;
|
|
}
|
|
|
|
DirectiveNode* directive = (DirectiveNode*)statement.get();
|
|
if (!token->hasAllocatedString && directive->value().equals("use strict")) {
|
|
this->scopeContexts.back()->m_isStrict = this->context->strict = true;
|
|
if (firstRestricted) {
|
|
this->throwUnexpectedToken(&firstRestricted, Messages::StrictOctalLiteral);
|
|
}
|
|
} else {
|
|
if (!firstRestricted && token->octal) {
|
|
firstRestricted = *token;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ECMA-262 14.3 Method Definitions
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseGetterMethod(ASTBuilder& builder)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expect(LeftParenthesis);
|
|
this->expect(RightParenthesis);
|
|
|
|
bool isGenerator = false;
|
|
ParseFormalParametersResult params;
|
|
const bool previousAllowYield = this->context->allowYield;
|
|
const bool previousInArrowFunction = this->context->inArrowFunction;
|
|
const bool previousAllowSuperProperty = this->context->allowSuperProperty;
|
|
|
|
this->context->allowYield = true;
|
|
this->context->inArrowFunction = false;
|
|
this->context->allowSuperProperty = true;
|
|
|
|
pushScopeContext(AtomicString());
|
|
extractNamesFromFunctionParams(params);
|
|
ASTNode method = this->parsePropertyMethod(builder, params);
|
|
|
|
this->context->allowYield = previousAllowYield;
|
|
this->context->inArrowFunction = previousInArrowFunction;
|
|
this->context->allowSuperProperty = previousAllowSuperProperty;
|
|
|
|
scopeContexts.back()->m_paramsStartLOC.index = node.index;
|
|
scopeContexts.back()->m_paramsStartLOC.column = node.column;
|
|
scopeContexts.back()->m_paramsStartLOC.line = node.line;
|
|
scopeContexts.back()->m_allowSuperProperty = true;
|
|
|
|
scopeContexts.back()->m_nodeType = ASTNodeType::FunctionExpression;
|
|
scopeContexts.back()->m_isGenerator = isGenerator;
|
|
return this->finalize(node, builder.createFunctionExpressionNode(popScopeContext(node), isGenerator, this->subCodeBlockIndex));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseSetterMethod(ASTBuilder& builder)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
bool isGenerator = false;
|
|
const bool previousAllowYield = this->context->allowYield;
|
|
const bool previousInArrowFunction = this->context->inArrowFunction;
|
|
const bool previousAllowSuperProperty = this->context->allowSuperProperty;
|
|
|
|
this->context->allowYield = true;
|
|
this->context->allowSuperProperty = true;
|
|
this->context->inArrowFunction = false;
|
|
|
|
this->expect(LeftParenthesis);
|
|
pushScopeContext(AtomicString());
|
|
ParseFormalParametersResult formalParameters = this->parseFormalParameters();
|
|
if (formalParameters.params.size() != 1) {
|
|
this->throwError(Messages::BadSetterArity);
|
|
} else if (formalParameters.params[0]->type() == ASTNodeType::RestElement) {
|
|
this->throwError(Messages::BadSetterRestParameter);
|
|
}
|
|
extractNamesFromFunctionParams(formalParameters);
|
|
ASTNode method = this->parsePropertyMethod(builder, formalParameters);
|
|
|
|
this->context->allowYield = previousAllowYield;
|
|
this->context->allowSuperProperty = previousAllowSuperProperty;
|
|
this->context->inArrowFunction = previousInArrowFunction;
|
|
|
|
scopeContexts.back()->m_paramsStartLOC.index = node.index;
|
|
scopeContexts.back()->m_paramsStartLOC.column = node.column;
|
|
scopeContexts.back()->m_paramsStartLOC.line = node.line;
|
|
scopeContexts.back()->m_allowSuperProperty = true;
|
|
|
|
scopeContexts.back()->m_nodeType = ASTNodeType::FunctionExpression;
|
|
scopeContexts.back()->m_isGenerator = isGenerator;
|
|
return this->finalize(node, builder.createFunctionExpressionNode(popScopeContext(node), isGenerator, this->subCodeBlockIndex));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseGeneratorMethod(ASTBuilder& builder)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
bool isGenerator = true;
|
|
const bool previousAllowYield = this->context->allowYield;
|
|
const bool previousInArrowFunction = this->context->inArrowFunction;
|
|
const bool previousAllowSuperProperty = this->context->allowSuperProperty;
|
|
|
|
this->context->allowYield = false;
|
|
this->context->allowSuperProperty = true;
|
|
this->context->inArrowFunction = false;
|
|
|
|
this->expect(LeftParenthesis);
|
|
pushScopeContext(AtomicString());
|
|
ParseFormalParametersResult formalParameters = this->parseFormalParameters();
|
|
extractNamesFromFunctionParams(formalParameters);
|
|
ASTNode method = this->parsePropertyMethod(builder, formalParameters);
|
|
|
|
this->context->allowYield = previousAllowYield;
|
|
this->context->allowSuperProperty = previousAllowSuperProperty;
|
|
this->context->inArrowFunction = previousInArrowFunction;
|
|
|
|
scopeContexts.back()->m_paramsStartLOC.index = node.index;
|
|
scopeContexts.back()->m_paramsStartLOC.column = node.column;
|
|
scopeContexts.back()->m_paramsStartLOC.line = node.line;
|
|
scopeContexts.back()->m_allowSuperProperty = true;
|
|
|
|
scopeContexts.back()->m_nodeType = ASTNodeType::FunctionExpression;
|
|
scopeContexts.back()->m_isGenerator = isGenerator;
|
|
return this->finalize(node, builder.createFunctionExpressionNode(popScopeContext(node), isGenerator, this->subCodeBlockIndex));
|
|
}
|
|
|
|
// ECMA-262 14.4 Generator Function Definitions
|
|
|
|
bool isStartOfExpression()
|
|
{
|
|
bool start = true;
|
|
|
|
if (this->lookahead.type == Token::PunctuatorToken) {
|
|
switch (this->lookahead.valuePunctuatorKind) {
|
|
case PunctuatorKind::LeftSquareBracket:
|
|
case PunctuatorKind::LeftParenthesis:
|
|
case PunctuatorKind::LeftBrace:
|
|
case PunctuatorKind::Plus:
|
|
case PunctuatorKind::Minus:
|
|
case PunctuatorKind::ExclamationMark:
|
|
case PunctuatorKind::Wave:
|
|
case PunctuatorKind::PlusPlus:
|
|
case PunctuatorKind::MinusMinus:
|
|
case PunctuatorKind::Divide:
|
|
case PunctuatorKind::DivideEqual:
|
|
start = true;
|
|
break;
|
|
default:
|
|
start = false;
|
|
break;
|
|
}
|
|
} else if (this->lookahead.type == Token::KeywordToken) {
|
|
switch (this->lookahead.valueKeywordKind) {
|
|
case KeywordKind::ClassKeyword:
|
|
case KeywordKind::DeleteKeyword:
|
|
case KeywordKind::FunctionKeyword:
|
|
case KeywordKind::LetKeyword:
|
|
case KeywordKind::NewKeyword:
|
|
case KeywordKind::SuperKeyword:
|
|
case KeywordKind::ThisKeyword:
|
|
case KeywordKind::TypeofKeyword:
|
|
case KeywordKind::VoidKeyword:
|
|
case KeywordKind::YieldKeyword:
|
|
start = true;
|
|
break;
|
|
default:
|
|
start = false;
|
|
}
|
|
}
|
|
|
|
return start;
|
|
}
|
|
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseYieldExpression(ASTBuilder& builder)
|
|
{
|
|
if (this->context->inParameterParsing) {
|
|
this->throwError("Cannot use yield expression within parameters");
|
|
}
|
|
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(YieldKeyword);
|
|
|
|
ASTNode exprNode;
|
|
bool delegate = false;
|
|
|
|
if (!this->hasLineTerminator) {
|
|
const bool previousAllowYield = this->context->allowYield;
|
|
this->context->allowYield = false;
|
|
delegate = this->match(Multiply);
|
|
|
|
if (delegate) {
|
|
this->nextToken();
|
|
exprNode = this->parseAssignmentExpression<ASTBuilder, false>(builder);
|
|
} else if (isStartOfExpression()) {
|
|
exprNode = this->parseAssignmentExpression<ASTBuilder, false>(builder);
|
|
}
|
|
this->context->allowYield = previousAllowYield;
|
|
}
|
|
|
|
scopeContexts.back()->m_hasYield = true;
|
|
|
|
return this->finalize(node, builder.createYieldExpressionNode(exprNode, delegate));
|
|
}
|
|
|
|
// ECMA-262 14.5 Class Definitions
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseClassElement(ASTBuilder& builder, ASTNode& constructor, bool hasSuperClass)
|
|
{
|
|
ALLOC_TOKEN(token);
|
|
*token = this->lookahead;
|
|
MetaNode node = this->createNode();
|
|
|
|
ClassElementNode::Kind kind = ClassElementNode::Kind::None;
|
|
|
|
ASTNode keyNode;
|
|
ASTNode value;
|
|
StringView keyString;
|
|
|
|
bool computed = false;
|
|
bool isStatic = false;
|
|
bool isGenerator = false;
|
|
|
|
if (this->match(Multiply)) {
|
|
this->nextToken();
|
|
} else {
|
|
computed = this->match(LeftSquareBracket);
|
|
keyNode = this->parseObjectPropertyKey(builder, keyString);
|
|
|
|
if (token->type == Token::KeywordToken && (this->qualifiedPropertyName(&this->lookahead) || this->match(Multiply)) && token->valueStringLiteral() == "static") {
|
|
*token = this->lookahead;
|
|
isStatic = true;
|
|
computed = this->match(LeftSquareBracket);
|
|
if (this->match(Multiply)) {
|
|
this->nextToken();
|
|
} else {
|
|
keyNode = this->parseObjectPropertyKey(builder, keyString);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool lookaheadPropertyKey = this->qualifiedPropertyName(&this->lookahead);
|
|
if (token->type == Token::IdentifierToken) {
|
|
if (token->valueStringLiteral() == "get" && lookaheadPropertyKey) {
|
|
kind = ClassElementNode::Kind::Get;
|
|
computed = this->match(LeftSquareBracket);
|
|
keyNode = this->parseObjectPropertyKey(builder, keyString);
|
|
this->context->allowYield = false;
|
|
value = this->parseGetterMethod(builder);
|
|
} else if (token->valueStringLiteral() == "set" && lookaheadPropertyKey) {
|
|
kind = ClassElementNode::Kind::Set;
|
|
computed = this->match(LeftSquareBracket);
|
|
keyNode = this->parseObjectPropertyKey(builder, keyString);
|
|
value = this->parseSetterMethod(builder);
|
|
}
|
|
} else if (lookaheadPropertyKey && token->type == Token::PunctuatorToken && token->valuePunctuatorKind == Multiply) {
|
|
kind = ClassElementNode::Kind::Method;
|
|
computed = this->match(LeftSquareBracket);
|
|
keyNode = this->parseObjectPropertyKey(builder, keyString);
|
|
value = this->parseGeneratorMethod(builder);
|
|
isGenerator = true;
|
|
}
|
|
|
|
if (kind == ClassElementNode::Kind::None && keyNode && this->match(LeftParenthesis)) {
|
|
kind = ClassElementNode::Kind::Method;
|
|
bool allowSuperCall = false;
|
|
if (hasSuperClass) {
|
|
if (keyNode && this->isPropertyKey(keyNode.get(), keyString, "constructor")) {
|
|
allowSuperCall = true;
|
|
}
|
|
}
|
|
value = this->parsePropertyMethodFunction(builder, allowSuperCall);
|
|
}
|
|
|
|
if (kind == ClassElementNode::Kind::None) {
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
|
|
if (isStatic && !computed && this->isPropertyKey(keyNode.get(), keyString, "prototype")) {
|
|
this->throwUnexpectedToken(token, Messages::StaticPrototype);
|
|
}
|
|
if (!isStatic && !computed && this->isPropertyKey(keyNode.get(), keyString, "constructor")) {
|
|
if (kind != ClassElementNode::Kind::Method) {
|
|
this->throwUnexpectedToken(token, Messages::ConstructorSpecialMethod);
|
|
}
|
|
if (isGenerator) {
|
|
this->throwUnexpectedToken(token, Messages::ConstructorGenerator);
|
|
}
|
|
if (constructor) {
|
|
this->throwUnexpectedToken(token, Messages::DuplicateConstructor);
|
|
} else {
|
|
if (!this->isParsingSingleFunction) {
|
|
this->lastPoppedScopeContext->m_functionName = escargotContext->staticStrings().constructor;
|
|
this->lastPoppedScopeContext->m_isClassConstructor = true;
|
|
this->lastPoppedScopeContext->m_isDerivedClassConstructor = hasSuperClass;
|
|
}
|
|
constructor = value;
|
|
ASTNode empty;
|
|
return empty;
|
|
}
|
|
}
|
|
|
|
if (!this->isParsingSingleFunction) {
|
|
if (keyNode->type() == Identifier) {
|
|
this->lastPoppedScopeContext->m_functionName = keyNode->asIdentifier()->name();
|
|
}
|
|
if (isStatic) {
|
|
this->lastPoppedScopeContext->m_isClassStaticMethod = true;
|
|
} else {
|
|
this->lastPoppedScopeContext->m_isClassMethod = true;
|
|
}
|
|
}
|
|
|
|
return this->finalize(node, builder.createClassElementNode(keyNode.get(), value.get(), kind, computed, isStatic));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseClassBody(ASTBuilder& builder, bool hasSuperClass, MetaNode& endNode)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
ASTNodeVector body;
|
|
ASTNode constructor;
|
|
|
|
this->expect(LeftBrace);
|
|
while (!this->match(RightBrace)) {
|
|
if (this->match(SemiColon)) {
|
|
this->nextToken();
|
|
} else {
|
|
ASTNode classElement = this->parseClassElement(builder, constructor, hasSuperClass);
|
|
if (classElement) {
|
|
body.push_back(classElement);
|
|
}
|
|
}
|
|
}
|
|
endNode = this->createNode();
|
|
endNode.index++; // advancing for '{'
|
|
this->expect(RightBrace);
|
|
|
|
return this->finalize(node, builder.createClassBodyNode(std::move(body), constructor));
|
|
}
|
|
|
|
template <class ASTBuilder, typename ClassType>
|
|
ASTPassNode classDeclaration(ASTBuilder& builder, bool identifierIsOptional)
|
|
{
|
|
bool previousStrict = this->context->strict;
|
|
this->context->strict = true;
|
|
MetaNode startNode = this->createNode();
|
|
this->expectKeyword(ClassKeyword);
|
|
|
|
ASTNode idNode;
|
|
AtomicString id;
|
|
|
|
if (!identifierIsOptional || this->lookahead.type == Token::IdentifierToken) {
|
|
idNode = this->parseVariableIdentifier(builder);
|
|
id = idNode->asIdentifier()->name();
|
|
}
|
|
|
|
if (!identifierIsOptional && id.string()->length()) {
|
|
addDeclaredNameIntoContext(id, this->lexicalBlockIndex, KeywordKind::LetKeyword);
|
|
}
|
|
|
|
bool hasSuperClass = false;
|
|
ASTNode superClass;
|
|
if (this->matchKeyword(ExtendsKeyword)) {
|
|
hasSuperClass = true;
|
|
this->nextToken();
|
|
superClass = this->isolateCoverGrammar(builder, &Parser::parseLeftHandSideExpressionAllowCall<ASTBuilder>);
|
|
}
|
|
|
|
ParserBlockContext classBlockContext = openBlock();
|
|
if (id.string()->length()) {
|
|
addDeclaredNameIntoContext(id, this->lexicalBlockIndex, KeywordKind::ConstKeyword);
|
|
}
|
|
|
|
MetaNode endNode;
|
|
ASTNode classBody = this->parseClassBody(builder, hasSuperClass, endNode);
|
|
|
|
this->context->strict = previousStrict;
|
|
closeBlock(classBlockContext);
|
|
|
|
return this->finalize(startNode, builder.template createClass<ClassType>(idNode, superClass, classBody, classBlockContext.childLexicalBlockIndex, StringView(this->scanner->source, startNode.index, endNode.index)));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseClassDeclaration(ASTBuilder& builder)
|
|
{
|
|
return classDeclaration<ASTBuilder, ClassDeclarationNode>(builder, false);
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseClassExpression(ASTBuilder& builder)
|
|
{
|
|
return classDeclaration<ASTBuilder, ClassExpressionNode>(builder, true);
|
|
}
|
|
|
|
// ECMA-262 15.1 Scripts
|
|
// ECMA-262 15.2 Modules
|
|
|
|
PassRefPtr<ProgramNode> parseProgram(NodeGenerator& builder)
|
|
{
|
|
MetaNode startNode = this->createNode();
|
|
pushScopeContext(new ASTFunctionScopeContext(this->context->strict));
|
|
this->context->allowLexicalDeclaration = true;
|
|
RefPtr<StatementContainer> container = StatementContainer::create();
|
|
this->parseDirectivePrologues(container.get());
|
|
StatementNode* referNode = nullptr;
|
|
while (this->startMarker.index < this->scanner->length) {
|
|
referNode = container->appendChild(this->parseStatementListItem(builder).get(), referNode);
|
|
}
|
|
scopeContexts.back()->m_bodyStartLOC.line = startNode.line;
|
|
scopeContexts.back()->m_bodyStartLOC.column = startNode.column;
|
|
scopeContexts.back()->m_bodyStartLOC.index = startNode.index;
|
|
|
|
MetaNode endNode = this->createNode();
|
|
#ifndef NDEBUG
|
|
scopeContexts.back()->m_bodyEndLOC.line = endNode.line;
|
|
scopeContexts.back()->m_bodyEndLOC.column = endNode.column;
|
|
#endif
|
|
scopeContexts.back()->m_bodyEndLOC.index = endNode.index;
|
|
return this->finalize(startNode, new ProgramNode(container.get(), scopeContexts.back(), this->moduleData));
|
|
}
|
|
|
|
PassRefPtr<FunctionNode> parseScriptFunction(NodeGenerator& builder)
|
|
{
|
|
ASSERT(this->isParsingSingleFunction);
|
|
|
|
MetaNode node = this->createNode();
|
|
RefPtr<StatementContainer> params = this->parseFunctionParameters();
|
|
RefPtr<BlockStatementNode> body = this->parseFunctionBody(builder);
|
|
|
|
return this->finalize(node, new FunctionNode(params.get(), body.get()));
|
|
}
|
|
|
|
PassRefPtr<FunctionNode> parseScriptArrowFunction(NodeGenerator& builder, bool hasArrowParameterPlaceHolder)
|
|
{
|
|
ASSERT(this->isParsingSingleFunction);
|
|
|
|
MetaNode node = this->createNode();
|
|
RefPtr<StatementContainer> params;
|
|
RefPtr<BlockStatementNode> body;
|
|
|
|
// generate parameter statements
|
|
if (hasArrowParameterPlaceHolder) {
|
|
params = parseFunctionParameters();
|
|
} else {
|
|
RefPtr<Node> param = this->parseConditionalExpression(builder);
|
|
ASSERT(param->type() == Identifier);
|
|
params = StatementContainer::create();
|
|
RefPtr<InitializeParameterExpressionNode> init = this->finalize(node, new InitializeParameterExpressionNode(param.get(), 0));
|
|
RefPtr<Node> statement = this->finalize(node, new ExpressionStatementNode(init.get()));
|
|
params->appendChild(statement->asStatement());
|
|
}
|
|
this->expect(Arrow);
|
|
this->context->inArrowFunction = true;
|
|
|
|
// generate body statements
|
|
if (this->match(LeftBrace)) {
|
|
body = parseFunctionBody(builder);
|
|
} else {
|
|
MetaNode nodeStart = this->createNode();
|
|
RefPtr<StatementContainer> container = StatementContainer::create();
|
|
|
|
auto previousLabelSet = this->context->labelSet;
|
|
bool previousInIteration = this->context->inIteration;
|
|
bool previousInSwitch = this->context->inSwitch;
|
|
bool previousInFunctionBody = this->context->inFunctionBody;
|
|
|
|
this->context->labelSet.clear();
|
|
this->context->inIteration = false;
|
|
this->context->inSwitch = false;
|
|
this->context->inFunctionBody = true;
|
|
|
|
bool previousStrict = this->context->strict;
|
|
bool previousAllowYield = this->context->allowYield;
|
|
this->context->allowYield = true;
|
|
|
|
RefPtr<Node> expr = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression<NodeGenerator, false>);
|
|
|
|
container->appendChild(this->finalize(nodeStart, new ReturnStatementNode(expr.get())), nullptr);
|
|
|
|
/*
|
|
if (this->context->strict && list.firstRestricted) {
|
|
this->throwUnexpectedToken(list.firstRestricted, list.message);
|
|
}
|
|
if (this->context->strict && list.stricted) {
|
|
this->throwUnexpectedToken(list.stricted, list.message);
|
|
}
|
|
*/
|
|
|
|
this->context->strict = previousStrict;
|
|
this->context->allowYield = previousAllowYield;
|
|
|
|
this->context->labelSet = previousLabelSet;
|
|
this->context->inIteration = previousInIteration;
|
|
this->context->inSwitch = previousInSwitch;
|
|
this->context->inFunctionBody = previousInFunctionBody;
|
|
|
|
body = this->finalize(nodeStart, new BlockStatementNode(container.get()));
|
|
}
|
|
|
|
return this->finalize(node, new FunctionNode(params.get(), body.get()));
|
|
}
|
|
|
|
// ECMA-262 15.2.2 Imports
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseModuleSpecifier(ASTBuilder& builder)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
if (this->lookahead.type != Token::StringLiteralToken) {
|
|
this->throwError(Messages::InvalidModuleSpecifier);
|
|
}
|
|
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
|
|
// const raw = this->getTokenRaw(token);
|
|
ASSERT(token->type == Token::StringLiteralToken);
|
|
return this->finalize(node, builder.createLiteralNode(token->valueStringLiteralForAST()));
|
|
}
|
|
|
|
// import {<foo as bar>} ...;
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseImportSpecifier(ASTBuilder& builder)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
ASTNode local;
|
|
ASTNode imported = this->parseIdentifierName(builder);
|
|
if (this->matchContextualKeyword("as")) {
|
|
this->nextToken();
|
|
local = this->parseVariableIdentifier(builder);
|
|
} else {
|
|
local = imported;
|
|
}
|
|
|
|
return this->finalize(node, builder.createImportSpecifierNode(local, imported));
|
|
}
|
|
|
|
// {foo, bar as bas}
|
|
template <class ASTBuilder>
|
|
ASTNodeVector parseNamedImports(ASTBuilder& builder)
|
|
{
|
|
this->expect(PunctuatorKind::LeftBrace);
|
|
ASTNodeVector specifiers;
|
|
while (!this->match(PunctuatorKind::RightBrace)) {
|
|
specifiers.push_back(this->parseImportSpecifier(builder));
|
|
if (!this->match(PunctuatorKind::RightBrace)) {
|
|
this->expect(PunctuatorKind::Comma);
|
|
}
|
|
}
|
|
this->expect(PunctuatorKind::RightBrace);
|
|
return specifiers;
|
|
}
|
|
|
|
// import <foo> ...;
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseImportDefaultSpecifier(ASTBuilder& builder)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
ASTNode local = this->parseIdentifierName(builder);
|
|
return this->finalize(node, builder.createImportDefaultSpecifierNode(local));
|
|
}
|
|
|
|
// import <* as foo> ...;
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseImportNamespaceSpecifier(ASTBuilder& builder)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
this->expect(PunctuatorKind::Multiply);
|
|
if (!this->matchContextualKeyword("as")) {
|
|
this->throwError(Messages::NoAsAfterImportNamespace);
|
|
}
|
|
this->nextToken();
|
|
ASTNode local = this->parseIdentifierName(builder);
|
|
return this->finalize(node, builder.createImportNamespaceSpecifierNode(local));
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseImportDeclaration(ASTBuilder& builder)
|
|
{
|
|
if (this->context->inFunctionBody) {
|
|
this->throwError(Messages::IllegalImportDeclaration);
|
|
}
|
|
|
|
if (this->lexicalBlockIndex != 0) {
|
|
this->throwError(Messages::IllegalImportDeclaration);
|
|
}
|
|
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(KeywordKind::ImportKeyword);
|
|
|
|
ASTNode src;
|
|
|
|
Script::ImportEntryVector importEntrys;
|
|
ASTNodeVector specifiers;
|
|
if (this->lookahead.type == Token::StringLiteralToken) {
|
|
// import 'foo';
|
|
src = this->parseModuleSpecifier(builder);
|
|
} else {
|
|
if (this->match(PunctuatorKind::LeftBrace)) {
|
|
// import {bar}
|
|
specifiers = this->parseNamedImports(builder);
|
|
|
|
if (builder.isNodeGenerator()) {
|
|
for (size_t i = 0; i < specifiers.size(); i++) {
|
|
Script::ImportEntry entry;
|
|
entry.m_importName = specifiers[i]->asImportSpecifier()->imported()->name();
|
|
entry.m_localName = specifiers[i]->asImportSpecifier()->local()->name();
|
|
importEntrys.push_back(entry);
|
|
}
|
|
}
|
|
} else if (this->match(PunctuatorKind::Multiply)) {
|
|
// import * as foo
|
|
specifiers.push_back(this->parseImportNamespaceSpecifier(builder));
|
|
|
|
if (builder.isNodeGenerator()) {
|
|
Script::ImportEntry entry;
|
|
entry.m_importName = this->escargotContext->staticStrings().asciiTable[(unsigned char)'*'];
|
|
entry.m_localName = ((ImportNamespaceSpecifierNode*)specifiers.back().get())->local()->name();
|
|
importEntrys.push_back(entry);
|
|
}
|
|
} else if (this->isIdentifierName(&this->lookahead) && !this->matchKeyword(KeywordKind::DefaultKeyword)) {
|
|
// import foo
|
|
specifiers.push_back(this->parseImportDefaultSpecifier(builder));
|
|
|
|
if (builder.isNodeGenerator()) {
|
|
Script::ImportEntry entry;
|
|
entry.m_importName = this->escargotContext->staticStrings().stringDefault;
|
|
entry.m_localName = ((ImportDefaultSpecifierNode*)specifiers.back().get())->local()->name();
|
|
importEntrys.push_back(entry);
|
|
}
|
|
|
|
if (this->match(PunctuatorKind::Comma)) {
|
|
this->nextToken();
|
|
if (this->match(PunctuatorKind::Multiply)) {
|
|
// import foo, * as foo
|
|
specifiers.push_back(this->parseImportNamespaceSpecifier(builder));
|
|
|
|
if (builder.isNodeGenerator()) {
|
|
Script::ImportEntry entry;
|
|
entry.m_importName = this->escargotContext->staticStrings().asciiTable[(unsigned char)'*'];
|
|
entry.m_localName = ((ImportNamespaceSpecifierNode*)specifiers.back().get())->local()->name();
|
|
importEntrys.push_back(entry);
|
|
}
|
|
} else if (this->match(PunctuatorKind::LeftBrace)) {
|
|
// import foo, {bar}
|
|
auto v = this->parseNamedImports(builder);
|
|
|
|
if (builder.isNodeGenerator()) {
|
|
for (size_t i = 0; i < v.size(); i++) {
|
|
Script::ImportEntry entry;
|
|
entry.m_importName = ((ImportSpecifierNode*)v[i].get())->imported()->name();
|
|
entry.m_localName = ((ImportSpecifierNode*)v[i].get())->local()->name();
|
|
importEntrys.push_back(entry);
|
|
}
|
|
|
|
specifiers.insert(specifiers.end(), v.begin(), v.end());
|
|
}
|
|
} else {
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
}
|
|
} else {
|
|
ALLOC_TOKEN(token);
|
|
this->nextToken(token);
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
|
|
if (!this->matchContextualKeyword("from")) {
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
this->nextToken();
|
|
src = this->parseModuleSpecifier(builder);
|
|
}
|
|
this->consumeSemicolon();
|
|
|
|
if (builder.isNodeGenerator()) {
|
|
for (size_t i = 0; i < importEntrys.size(); i++) {
|
|
importEntrys[i].m_moduleRequest = src->asLiteral()->value().asString();
|
|
addDeclaredNameIntoContext(importEntrys[i].m_localName, this->lexicalBlockIndex, KeywordKind::ConstKeyword);
|
|
this->moduleData->m_importEntries.insert(this->moduleData->m_importEntries.size(), importEntrys[i]);
|
|
}
|
|
}
|
|
|
|
return this->finalize(node, builder.createImportDeclarationNode(std::move(specifiers), src));
|
|
}
|
|
|
|
// ECMA-262 15.2.3 Exports
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseExportSpecifier(ASTBuilder& builder)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
ASTNode local;
|
|
local = this->parseIdentifierName(builder);
|
|
|
|
ASTNode exported = local;
|
|
if (this->matchContextualKeyword("as")) {
|
|
this->nextToken();
|
|
exported = this->parseIdentifierName(builder);
|
|
}
|
|
|
|
return this->finalize(node, builder.createExportSpecifierNode(local, exported));
|
|
}
|
|
|
|
void addExportDeclarationEntry(Script::ExportEntry ee)
|
|
{
|
|
// http://www.ecma-international.org/ecma-262/6.0/#sec-parsemodule
|
|
// If ee.[[ModuleRequest]] is null, then
|
|
if (!ee.m_moduleRequest.hasValue()) {
|
|
// If ee.[[LocalName]] is not an element of importedBoundNames, then
|
|
size_t localNameExistInImportedBoundNamesIndex = SIZE_MAX;
|
|
for (size_t i = 0; i < this->moduleData->m_importEntries.size(); i++) {
|
|
if (ee.m_localName == this->moduleData->m_importEntries[i].m_localName) {
|
|
localNameExistInImportedBoundNamesIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (localNameExistInImportedBoundNamesIndex == SIZE_MAX) {
|
|
// Append ee to localExportEntries.
|
|
this->moduleData->m_localExportEntries.push_back(ee);
|
|
} else {
|
|
// Let ie be the element of importEntries whose [[LocalName]] is the same as ee.[[LocalName]].
|
|
Script::ImportEntry ie = this->moduleData->m_importEntries[localNameExistInImportedBoundNamesIndex];
|
|
// If ie.[[ImportName]] is "*", then
|
|
if (ie.m_importName == this->escargotContext->staticStrings().asciiTable[(unsigned char)'*']) {
|
|
// Assert: this is a re-export of an imported module namespace object.
|
|
// Append ee to localExportEntries.
|
|
this->moduleData->m_localExportEntries.push_back(ee);
|
|
} else {
|
|
// Else, this is a re-export of a single name
|
|
// Append to indirectExportEntries the Record {[[ModuleRequest]]: ie.[[ModuleRequest]], [[ImportName]]: ie.[[ImportName]], [[LocalName]]: null, [[ExportName]]: ee.[[ExportName]] }.
|
|
Script::ExportEntry newEntry;
|
|
newEntry.m_moduleRequest = ie.m_moduleRequest;
|
|
newEntry.m_importName = ie.m_importName;
|
|
newEntry.m_exportName = ee.m_exportName;
|
|
|
|
this->moduleData->m_indirectExportEntries.push_back(newEntry);
|
|
}
|
|
}
|
|
} else if (ee.m_importName == this->escargotContext->staticStrings().asciiTable[(unsigned char)'*']) {
|
|
// Else, if ee.[[ImportName]] is "*", then
|
|
this->moduleData->m_starExportEntries.push_back(ee);
|
|
} else {
|
|
// Append ee to indirectExportEntries.
|
|
this->moduleData->m_indirectExportEntries.push_back(ee);
|
|
}
|
|
}
|
|
|
|
template <class ASTBuilder>
|
|
ASTPassNode parseExportDeclaration(ASTBuilder& builder)
|
|
{
|
|
if (this->context->inFunctionBody) {
|
|
this->throwError(Messages::IllegalExportDeclaration);
|
|
}
|
|
|
|
if (this->lexicalBlockIndex != 0) {
|
|
this->throwError(Messages::IllegalExportDeclaration);
|
|
}
|
|
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(KeywordKind::ExportKeyword);
|
|
|
|
ASTNode exportDeclaration;
|
|
if (this->matchKeyword(KeywordKind::DefaultKeyword)) {
|
|
// export default ...
|
|
|
|
this->nextToken();
|
|
if (this->matchKeyword(KeywordKind::FunctionKeyword)) {
|
|
// export default function foo () {}
|
|
// export default function () {}
|
|
ASTNode declaration = this->parseFunctionExpression(builder);
|
|
|
|
if (builder.isNodeGenerator()) {
|
|
Script::ExportEntry entry;
|
|
entry.m_exportName = this->escargotContext->staticStrings().stringDefault;
|
|
entry.m_localName = declaration->asFunctionExpression()->scopeContext()->m_functionName.string()->length() ? declaration->asFunctionExpression()->scopeContext()->m_functionName : this->escargotContext->staticStrings().stringStarDefaultStar;
|
|
addDeclaredNameIntoContext(entry.m_localName.value(), this->lexicalBlockIndex, KeywordKind::LetKeyword);
|
|
addExportDeclarationEntry(entry);
|
|
exportDeclaration = this->finalize(node, builder.createExportDefaultDeclarationNode(declaration, entry.m_exportName.value(), entry.m_localName.value()));
|
|
}
|
|
} else if (this->matchKeyword(KeywordKind::ClassKeyword)) {
|
|
// export default class foo {}
|
|
auto classNode = this->parseClassExpression(builder);
|
|
|
|
if (builder.isNodeGenerator()) {
|
|
Script::ExportEntry entry;
|
|
entry.m_exportName = this->escargotContext->staticStrings().stringDefault;
|
|
entry.m_localName = classNode->asClassExpression()->classNode().id().get() ? classNode->asClassExpression()->classNode().id()->asIdentifier()->name() : this->escargotContext->staticStrings().stringStarDefaultStar;
|
|
addDeclaredNameIntoContext(entry.m_localName.value(), this->lexicalBlockIndex, KeywordKind::LetKeyword);
|
|
addExportDeclarationEntry(entry);
|
|
|
|
exportDeclaration = this->finalize(node, builder.createExportDefaultDeclarationNode(classNode, entry.m_exportName.value(), entry.m_localName.value()));
|
|
}
|
|
} else {
|
|
if (this->matchContextualKeyword("from")) {
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
// export default {};
|
|
// export default [];
|
|
// export default (1 + 2);
|
|
Script::ExportEntry entry;
|
|
entry.m_exportName = this->escargotContext->staticStrings().stringDefault;
|
|
entry.m_localName = this->escargotContext->staticStrings().stringStarDefaultStar;
|
|
ASTNode declaration = this->match(PunctuatorKind::LeftBrace) ? this->parseObjectInitializer(builder) : this->match(PunctuatorKind::LeftSquareBracket) ? this->parseArrayInitializer(builder) : this->parseAssignmentExpression<ASTBuilder, true>(builder);
|
|
exportDeclaration = this->finalize(node, builder.createExportDefaultDeclarationNode(declaration, entry.m_exportName.value(), entry.m_localName.value()));
|
|
|
|
addDeclaredNameIntoContext(entry.m_localName.value(), this->lexicalBlockIndex, KeywordKind::LetKeyword);
|
|
addExportDeclarationEntry(entry);
|
|
|
|
this->consumeSemicolon();
|
|
}
|
|
|
|
} else if (this->match(PunctuatorKind::Multiply)) {
|
|
// export * from 'foo';
|
|
this->nextToken();
|
|
if (!this->matchContextualKeyword("from")) {
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
this->nextToken();
|
|
if (this->lookahead.type != Token::StringLiteralToken) {
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
}
|
|
ASTNode src = this->parseModuleSpecifier(builder);
|
|
|
|
if (builder.isNodeGenerator()) {
|
|
Script::ExportEntry entry;
|
|
entry.m_exportName = this->escargotContext->staticStrings().stringDefault;
|
|
entry.m_moduleRequest = src->asLiteral()->value().asString();
|
|
entry.m_importName = this->escargotContext->staticStrings().asciiTable[(unsigned char)'*'];
|
|
addExportDeclarationEntry(entry);
|
|
}
|
|
|
|
exportDeclaration = this->finalize(node, builder.createExportAllDeclarationNode(src));
|
|
|
|
this->consumeSemicolon();
|
|
|
|
} else if (this->lookahead.type == Token::KeywordToken) {
|
|
// export var f = 1;
|
|
auto oldNameCallback = this->nameDeclaredCallback;
|
|
AtomicStringVector declaredNames;
|
|
if (builder.isNodeGenerator()) {
|
|
this->nameDeclaredCallback = [&declaredNames](AtomicString name, LexicalBlockIndex, KeywordKind, bool) {
|
|
declaredNames.push_back(name);
|
|
};
|
|
}
|
|
AtomicStringVector declaredName;
|
|
ASTNode declaration;
|
|
switch (this->lookahead.valueKeywordKind) {
|
|
case KeywordKind::LetKeyword:
|
|
case KeywordKind::ConstKeyword:
|
|
declaration = this->parseLexicalDeclaration(builder, false);
|
|
break;
|
|
case KeywordKind::VarKeyword:
|
|
case KeywordKind::ClassKeyword:
|
|
case KeywordKind::FunctionKeyword:
|
|
declaration = this->parseStatementListItem(builder);
|
|
break;
|
|
default:
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
break;
|
|
}
|
|
|
|
for (size_t i = 0; i < declaredNames.size(); i++) {
|
|
Script::ExportEntry entry;
|
|
entry.m_exportName = declaredNames[i];
|
|
entry.m_localName = declaredNames[i];
|
|
addExportDeclarationEntry(entry);
|
|
}
|
|
exportDeclaration = this->finalize(node, builder.createExportNamedDeclarationNode(declaration, ASTNodeVector(), nullptr));
|
|
this->nameDeclaredCallback = oldNameCallback;
|
|
} else {
|
|
ASTNodeVector specifiers;
|
|
ASTNode source;
|
|
bool isExportFromIdentifier = false;
|
|
|
|
this->expect(PunctuatorKind::LeftBrace);
|
|
while (!this->match(PunctuatorKind::RightBrace)) {
|
|
isExportFromIdentifier = isExportFromIdentifier || this->matchKeyword(KeywordKind::DefaultKeyword);
|
|
specifiers.push_back(this->parseExportSpecifier(builder));
|
|
if (!this->match(PunctuatorKind::RightBrace)) {
|
|
this->expect(PunctuatorKind::Comma);
|
|
}
|
|
}
|
|
this->expect(PunctuatorKind::RightBrace);
|
|
|
|
bool seenFrom = false;
|
|
if (this->matchContextualKeyword("from")) {
|
|
seenFrom = true;
|
|
// export {default} from 'foo';
|
|
// export {foo} from 'foo';
|
|
this->nextToken();
|
|
source = this->parseModuleSpecifier(builder);
|
|
this->consumeSemicolon();
|
|
} else if (isExportFromIdentifier) {
|
|
// export {default}; // missing fromClause
|
|
this->throwUnexpectedToken(&this->lookahead);
|
|
} else {
|
|
// export {foo};
|
|
this->consumeSemicolon();
|
|
}
|
|
|
|
if (builder.isNodeGenerator()) {
|
|
for (size_t i = 0; i < specifiers.size(); i++) {
|
|
Script::ExportEntry entry;
|
|
if (seenFrom) {
|
|
entry.m_exportName = specifiers[i]->asExportSpecifier()->exported()->name();
|
|
entry.m_importName = specifiers[i]->asExportSpecifier()->local()->name();
|
|
entry.m_moduleRequest = source->asLiteral()->value().asString();
|
|
} else {
|
|
entry.m_exportName = specifiers[i]->asExportSpecifier()->exported()->name();
|
|
entry.m_localName = specifiers[i]->asExportSpecifier()->local()->name();
|
|
}
|
|
|
|
addExportDeclarationEntry(entry);
|
|
}
|
|
}
|
|
|
|
exportDeclaration = this->finalize(node, builder.createExportNamedDeclarationNode(nullptr, std::move(specifiers), source));
|
|
}
|
|
|
|
return exportDeclaration;
|
|
}
|
|
};
|
|
|
|
RefPtr<ProgramNode> parseProgram(::Escargot::Context* ctx, StringView source, bool isModule, bool strictFromOutside, bool inWith, size_t stackRemain, bool allowSuperCallOutside, bool allowSuperPropertyOutside)
|
|
{
|
|
Parser parser(ctx, source, isModule, stackRemain);
|
|
NodeGenerator builder;
|
|
|
|
parser.context->strict = strictFromOutside;
|
|
parser.context->inWith = inWith;
|
|
parser.context->allowSuperCall = allowSuperCallOutside;
|
|
parser.context->allowSuperProperty = allowSuperPropertyOutside;
|
|
|
|
RefPtr<ProgramNode> nd = parser.parseProgram(builder);
|
|
return nd;
|
|
}
|
|
|
|
RefPtr<FunctionNode> parseSingleFunction(::Escargot::Context* ctx, InterpretedCodeBlock* codeBlock, ASTFunctionScopeContext*& scopeContext, size_t stackRemain)
|
|
{
|
|
Parser parser(ctx, codeBlock->src(), false, stackRemain, codeBlock->sourceElementStart());
|
|
NodeGenerator builder;
|
|
|
|
parser.trackUsingNames = false;
|
|
parser.context->allowLexicalDeclaration = true;
|
|
parser.context->allowSuperCall = true;
|
|
parser.context->allowSuperProperty = true;
|
|
parser.isParsingSingleFunction = true;
|
|
|
|
parser.codeBlock = codeBlock;
|
|
// when parsing for function call, childindex should be set to index 1
|
|
parser.childFunctionIndex = 1;
|
|
|
|
scopeContext = new ASTFunctionScopeContext(codeBlock->isStrict());
|
|
parser.pushScopeContext(scopeContext);
|
|
parser.context->allowYield = !codeBlock->isGenerator();
|
|
|
|
if (codeBlock->isArrowFunctionExpression()) {
|
|
return parser.parseScriptArrowFunction(builder, codeBlock->hasArrowParameterPlaceHolder());
|
|
}
|
|
return parser.parseScriptFunction(builder);
|
|
}
|
|
|
|
} // namespace esprima
|
|
} // namespace Escargot
|