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