/* 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 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 firstCoverInitializedNameError; std::vector catchClauseSimplyDeclaredVariableNames; std::vector> labelSet; // }; 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> scopeContexts; ASTFunctionScopeContext* lastPoppedScopeContext; AtomicString lastUsingName; std::function 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 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& 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 ALWAYS_INLINE PassRefPtr 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> ScannerResultVector; struct IsolateCoverGrammarContext { bool previousIsBindingElement; bool previousIsAssignmentTarget; std::unique_ptr 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 ALWAYS_INLINE ASTPassNode isolateCoverGrammar(ASTBuilder& builder, T parseFunction) { IsolateCoverGrammarContext grammarContext; startCoverGrammar(&grammarContext); ASTPassNode result = (this->*parseFunction)(builder); endIsolateCoverGrammar(&grammarContext); return result; } template ALWAYS_INLINE ASTPassNode isolateCoverGrammarWithFunctor(ASTBuilder& builder, T parseFunction) { IsolateCoverGrammarContext grammarContext; startCoverGrammar(&grammarContext); ASTPassNode result = parseFunction(); endIsolateCoverGrammar(&grammarContext); return result; } template ALWAYS_INLINE ASTPassNode inheritCoverGrammar(ASTBuilder& builder, T parseFunction) { IsolateCoverGrammarContext grammarContext; startCoverGrammar(&grammarContext); ASTPassNode result = (this->*parseFunction)(builder); endInheritCoverGrammar(&grammarContext); return result; } template 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 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); case LeftSquareBracket: return this->inheritCoverGrammar(builder, &Parser::parseArrayInitializer); case LeftBrace: return this->inheritCoverGrammar(builder, &Parser::parseObjectInitializer); 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 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 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 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 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(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 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 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 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); return this->finalize(this->startNode(startToken), builder.createAssignmentPatternNode(pattern.get(), right.get())); } return pattern; } bool parseFormalParameter(ParseFormalParametersResult& options) { RefPtr 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 ASTPassNode parseSpreadElement(ASTBuilder& builder) { MetaNode node = this->createNode(); this->expect(PunctuatorKind::PeriodPeriodPeriod); ASTNode arg; arg = this->inheritCoverGrammar(builder, &Parser::parseAssignmentExpression); if (arg->isAssignmentOperation()) { this->throwError(Messages::DefaultRestParameter); } return this->finalize(node, builder.createSpreadElementNode(arg.get())); } template 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(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)); 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 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); 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 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 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); this->expect(RightSquareBracket); } else { this->throwUnexpectedToken(token); } break; default: this->throwUnexpectedToken(token); } return key; } template 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); 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); 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 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 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 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); 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)); } 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 ASTNodeVector parseArguments(ASTBuilder& builder) { this->expect(LeftParenthesis); ASTNodeVector args; if (!this->match(RightParenthesis)) { while (true) { ASTNode expr; if (this->match(PeriodPeriodPeriod)) { expr = this->parseSpreadElement(builder); } else { expr = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression); } 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 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 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); 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 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); } else { exprNode = this->inheritCoverGrammar(builder, &Parser::parsePrimaryExpression); } 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); 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 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 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); } else { exprNode = this->inheritCoverGrammar(builder, &Parser::parsePrimaryExpression); } 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); 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 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); 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); 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 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); 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); 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); 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); 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); 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); 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); exprNode = this->finalize(node, builder.createUnaryExpressionTypeOfNode(subExpr.get())); this->context->isAssignmentTarget = false; this->context->isBindingElement = false; return exprNode.release(); } } return this->parseUpdateExpression(builder); } template ASTPassNode parseExponentiationExpression(ASTBuilder& builder) { ALLOC_TOKEN(startToken); *startToken = this->lookahead; ASTNode expr = this->inheritCoverGrammar(builder, &Parser::parseUnaryExpression); // TODO /* if (expr->type != Syntax.UnaryExpression && this->match('**')) { this->nextToken(); this->context->isAssignmentTarget = false; this->context->isBindingElement = false; const left = expr; const right = this->isolateCoverGrammar(this->parseExponentiationExpression); expr = this->finalize(this->startNode(startToken), new Node.BinaryExpression('**', left, right)); }*/ return expr; } // ECMA-262 12.6 Exponentiation Operators // ECMA-262 12.7 Multiplicative Operators // ECMA-262 12.8 Additive Operators // ECMA-262 12.9 Bitwise Shift Operators // ECMA-262 12.10 Relational Operators // ECMA-262 12.11 Equality Operators // ECMA-262 12.12 Binary Bitwise Operators // ECMA-262 12.13 Binary Logical Operators int binaryPrecedence(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 ASTPassNode parseBinaryExpression(ASTBuilder& builder) { ALLOC_TOKEN(startToken); *startToken = this->lookahead; ASTNode expr = this->inheritCoverGrammar(builder, &Parser::parseExponentiationExpression); 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); 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); 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 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 ASTPassNode parseConditionalExpression(ASTBuilder& builder) { ALLOC_TOKEN(startToken); *startToken = this->lookahead; ASTNode exprNode; exprNode = this->inheritCoverGrammar(builder, &Parser::parseBinaryExpression); if (this->match(GuessMark)) { ASTNode consequent; this->nextToken(); bool previousAllowIn = this->context->allowIn; this->context->allowIn = true; consequent = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression); this->context->allowIn = previousAllowIn; this->expect(Colon); ASTNode alternate = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression); 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 reparseConditionalExpression(Marker startMarker, RefPtr expr) { return expr; } RefPtr 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 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(); 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 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); 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); 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 ASTPassNode parseExpression(ASTBuilder& builder) { ALLOC_TOKEN(startToken); *startToken = this->lookahead; ASTNode exprNode; exprNode = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression); 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)); } exprNode = this->finalize(this->startNode(startToken), builder.createSequenceExpressionNode(std::move(expressions))); } return exprNode.release(); } // ECMA-262 13.2 Block template 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 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 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); } } else if ((!inFor && !isIdentifier) || this->match(Substitution)) { this->expect(Substitution); init = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression); } return this->finalize(node, builder.createVariableDeclaratorNode(kind, idNode.get(), init.get())); } template 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 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 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 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); 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 std::tuple 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 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 ASTPassNode parseEmptyStatement(ASTBuilder& builder) { this->expect(SemiColon); MetaNode node = this->createNode(); return this->finalize(node, builder.createEmptyStatementNode()); } // ECMA-262 13.5 Expression Statement template 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 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 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 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 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); 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)); } 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(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, 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 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 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 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 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 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 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 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 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 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 ASTPassNode parseFinallyClause(ASTBuilder& builder) { this->expectKeyword(FinallyKeyword); return this->parseBlock(builder); } template 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 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 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 parseFunctionParameters() { RefPtr 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 param = options.params.back(); switch (param->type()) { case Identifier: case AssignmentPattern: case ArrayPattern: case ObjectPattern: { RefPtr init = this->finalize(node, new InitializeParameterExpressionNode(param.get(), paramIndex)); RefPtr statement = this->finalize(node, new ExpressionStatementNode(init.get())); container->appendChild(statement->asStatement()); break; } case RestElement: { RefPtr 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 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 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 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 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 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 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 parseDirective() { ALLOC_TOKEN(token); *token = this->lookahead; StringView directiveValue; bool isLiteral = false; NodeGenerator newBuilder; MetaNode node = this->createNode(); RefPtr 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 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 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 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 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 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(builder); } else if (isStartOfExpression()) { exprNode = this->parseAssignmentExpression(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 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 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 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); } 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(idNode, superClass, classBody, classBlockContext.childLexicalBlockIndex, StringView(this->scanner->source, startNode.index, endNode.index))); } template ASTPassNode parseClassDeclaration(ASTBuilder& builder) { return classDeclaration(builder, false); } template ASTPassNode parseClassExpression(ASTBuilder& builder) { return classDeclaration(builder, true); } // ECMA-262 15.1 Scripts // ECMA-262 15.2 Modules PassRefPtr parseProgram(NodeGenerator& builder) { MetaNode startNode = this->createNode(); pushScopeContext(new ASTFunctionScopeContext(this->context->strict)); this->context->allowLexicalDeclaration = true; RefPtr 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 parseScriptFunction(NodeGenerator& builder) { ASSERT(this->isParsingSingleFunction); MetaNode node = this->createNode(); RefPtr params = this->parseFunctionParameters(); RefPtr body = this->parseFunctionBody(builder); return this->finalize(node, new FunctionNode(params.get(), body.get())); } PassRefPtr parseScriptArrowFunction(NodeGenerator& builder, bool hasArrowParameterPlaceHolder) { ASSERT(this->isParsingSingleFunction); MetaNode node = this->createNode(); RefPtr params; RefPtr body; // generate parameter statements if (hasArrowParameterPlaceHolder) { params = parseFunctionParameters(); } else { RefPtr param = this->parseConditionalExpression(builder); ASSERT(param->type() == Identifier); params = StatementContainer::create(); RefPtr init = this->finalize(node, new InitializeParameterExpressionNode(param.get(), 0)); RefPtr 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 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 expr = this->isolateCoverGrammar(builder, &Parser::parseAssignmentExpression); 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 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 {} ...; template 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 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 ...; template ASTPassNode parseImportDefaultSpecifier(ASTBuilder& builder) { MetaNode node = this->createNode(); ASTNode local = this->parseIdentifierName(builder); return this->finalize(node, builder.createImportDefaultSpecifierNode(local)); } // import <* as foo> ...; template 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 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 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 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(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 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 nd = parser.parseProgram(builder); return nd; } RefPtr 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