diff --git a/es2panda/compiler/core/dynamicContext.cpp b/es2panda/compiler/core/dynamicContext.cpp index 73e9e9ddcb08b589c09a48c9d822fe1497086fb4..f4402d572dbb1f83c5883664152aaac04e48e80a 100644 --- a/es2panda/compiler/core/dynamicContext.cpp +++ b/es2panda/compiler/core/dynamicContext.cpp @@ -36,7 +36,7 @@ DynamicContext::~DynamicContext() LabelContext::LabelContext(PandaGen *pg, const ir::LabelledStatement *labelledStmt) : DynamicContext(pg, LabelTarget(labelledStmt->Ident()->Name())), labelledStmt_(labelledStmt) { - if (!labelledStmt->Body()->IsBlockStatement()) { + if (!labelledStmt->Body()->IsBlockStatement() && !labelledStmt->Body()->IsIfStatement()) { return; } diff --git a/es2panda/parser/context/parserContext.h b/es2panda/parser/context/parserContext.h index 757a3d5be8ed1d6600a9a3da6fcdd3f53dc537b9..e1dc0af28ed50fede80e7f91a2e7e844f2538a68 100644 --- a/es2panda/parser/context/parserContext.h +++ b/es2panda/parser/context/parserContext.h @@ -124,6 +124,16 @@ public: return (status_ & ParserStatus::MODULE) != 0; } + bool IsInStatus(const ParserStatus status) const + { + return static_cast(status_ & status) == status; + } + + const util::StringView &Label() const + { + return label_; + } + const ParserContext *FindLabel(const util::StringView &label) const; private: diff --git a/es2panda/parser/parserImpl.h b/es2panda/parser/parserImpl.h index e73468a66427f1e910a1fc4261bc6cc2c7e85ebe..a6075544a18964e52b5bdb0cd437c063a072075a 100644 --- a/es2panda/parser/parserImpl.h +++ b/es2panda/parser/parserImpl.h @@ -392,6 +392,10 @@ private: void CheckLabelledFunction(const ir::Statement *node); void CheckDeclare(); + void checkBreakStatement(ir::Identifier *label) const; + void checkContinueStatement(ir::Identifier *label) const; + bool isIterationStatement(); + bool ParseDirective(ArenaVector *statements); void ParseDirectivePrologue(ArenaVector *statements); ArenaVector ParseStatementList(StatementParsingFlags flags = StatementParsingFlags::ALLOW_LEXICAL); diff --git a/es2panda/parser/statementParser.cpp b/es2panda/parser/statementParser.cpp index c1e63e01d48f552c25d9f51ea0a9d6d625c6ff3a..4b383c87a7f9067876e3b517796180a84b4b6837 100644 --- a/es2panda/parser/statementParser.cpp +++ b/es2panda/parser/statementParser.cpp @@ -116,6 +116,69 @@ void ParserImpl::CheckDeclare() } } +void ParserImpl::checkBreakStatement(ir::Identifier *label) const +{ + auto *iter = &context_; + while (iter) { + if (static_cast(iter->Status() & (ParserStatus::FUNCTION | + ParserStatus::IN_ITERATION | + ParserStatus::IN_SWITCH | + ParserStatus::IN_LABELED)) == ParserStatus::FUNCTION) { + ThrowSyntaxError("Jump target cannot cross function boundary."); + } + if (label == nullptr) { + if (iter->IsInStatus(ParserStatus::IN_SWITCH) || iter->IsInStatus(ParserStatus::IN_ITERATION)) { + return; + } + } else { + if (iter->IsInStatus(ParserStatus::IN_LABELED) && iter->Label() == label->Name()) { + return; + } + } + iter = iter->Prev(); + } + + if (label != nullptr) { + ThrowSyntaxError("Undefined label " + std::string(label->Name())); + } else { + ThrowSyntaxError("Illegal break statement"); + } +} + + +void ParserImpl::checkContinueStatement(ir::Identifier *label) const +{ + auto *iter = &context_; + while (iter) { + if (static_cast(iter->Status() & (ParserStatus::FUNCTION | + ParserStatus::IN_ITERATION | + ParserStatus::IN_SWITCH | + ParserStatus::IN_LABELED)) == ParserStatus::FUNCTION) { + ThrowSyntaxError("Jump target cannot cross function boundary."); + } + if (label == nullptr) { + if (iter->IsInStatus(ParserStatus::IN_ITERATION)) { + return; + } + } else { + if (iter->IsInStatus(ParserStatus::IN_LABELED) && iter->Label() == label->Name()) { + if (!iter->IsInStatus(ParserStatus::IN_ITERATION)) { + ThrowSyntaxError("Illegal continue statement: " + std::string(label->Name()) + + " does not denote an iteration statement"); + } + return; + } + } + iter = iter->Prev(); + } + + if (label != nullptr) { + ThrowSyntaxError("Undefined label " + std::string(label->Name())); + } else { + ThrowSyntaxError("Illegal continue statement: no surrounding iteration statement"); + } +} + ir::Statement *ParserImpl::ParseStatement(StatementParsingFlags flags) { bool isDeclare = false; @@ -792,15 +855,15 @@ ir::BreakStatement *ParserImpl::ParseBreakStatement() lexer_->GetToken().Type() == lexer::TokenType::EOS || lexer_->GetToken().NewLine() || lexer_->GetToken().Type() == lexer::TokenType::PUNCTUATOR_RIGHT_BRACE) { if (!allowBreak) { - if (Extension() == ScriptExtension::JS) { - ThrowSyntaxError("Illegal break statement"); - } if (Extension() == ScriptExtension::TS) { ThrowSyntaxError( "A 'break' statement can only be used within an " "enclosing iteration or switch statement"); } } + if (Extension() == ScriptExtension::JS) { + checkBreakStatement(nullptr); + } auto *breakStatement = AllocNode(); breakStatement->SetRange({startLoc, lexer_->GetToken().End()}); @@ -823,6 +886,9 @@ ir::BreakStatement *ParserImpl::ParseBreakStatement() auto *identNode = AllocNode(label, Allocator()); identNode->SetRange(lexer_->GetToken().Loc()); + if (Extension() == ScriptExtension::JS) { + checkBreakStatement(identNode); + } auto *breakStatement = AllocNode(identNode); breakStatement->SetRange({startLoc, lexer_->GetToken().End()}); @@ -842,9 +908,6 @@ ir::ContinueStatement *ParserImpl::ParseContinueStatement() } if (!(context_.Status() & ParserStatus::IN_ITERATION)) { - if (Extension() == ScriptExtension::JS) { - ThrowSyntaxError("Illegal continue statement"); - } if (Extension() == ScriptExtension::TS) { ThrowSyntaxError( "A 'continue' statement can only be used within an " @@ -857,6 +920,10 @@ ir::ContinueStatement *ParserImpl::ParseContinueStatement() lexer_->NextToken(); if (lexer_->GetToken().Type() == lexer::TokenType::PUNCTUATOR_SEMI_COLON) { + if (Extension() == ScriptExtension::JS) { + checkContinueStatement(nullptr); + } + auto *continueStatement = AllocNode(); continueStatement->SetRange({startLoc, lexer_->GetToken().End()}); lexer_->NextToken(); @@ -865,6 +932,10 @@ ir::ContinueStatement *ParserImpl::ParseContinueStatement() if (lexer_->GetToken().NewLine() || lexer_->GetToken().Type() == lexer::TokenType::EOS || lexer_->GetToken().Type() == lexer::TokenType::PUNCTUATOR_RIGHT_BRACE) { + if (Extension() == ScriptExtension::JS) { + checkContinueStatement(nullptr); + } + auto *continueStatement = AllocNode(); continueStatement->SetRange({startLoc, endLoc}); return continueStatement; @@ -877,12 +948,21 @@ ir::ContinueStatement *ParserImpl::ParseContinueStatement() const auto &label = lexer_->GetToken().Ident(); const ParserContext *labelCtx = context_.FindLabel(label); - if (!labelCtx || !(labelCtx->Status() & ParserStatus::IN_ITERATION)) { - ThrowSyntaxError("Undefined label"); + if (Extension() == ScriptExtension::TS) { + if (!labelCtx || !(labelCtx->Status() & ParserStatus::IN_ITERATION)) { + ThrowSyntaxError("Undefined label"); + } + } else { + if (!labelCtx) { + ThrowSyntaxError("Undefined label"); + } } auto *identNode = AllocNode(label, Allocator()); identNode->SetRange(lexer_->GetToken().Loc()); + if (Extension() == ScriptExtension::JS) { + checkContinueStatement(identNode); + } auto *continueStatement = AllocNode(identNode); continueStatement->SetRange({startLoc, lexer_->GetToken().End()}); @@ -1343,8 +1423,34 @@ ir::IfStatement *ParserImpl::ParseIfStatement() return ifStatement; } +bool ParserImpl::isIterationStatement() +{ + lexer_->NextToken(); + + switch (lexer_->GetToken().Type()) { + case lexer::TokenType::KEYW_DO: + case lexer::TokenType::KEYW_FOR: + case lexer::TokenType::KEYW_WHILE: { + return true; + } + case lexer::TokenType::LITERAL_IDENT: { + if (lexer_->Lookahead() == LEX_CHAR_COLON) { + lexer_->NextToken(); + return isIterationStatement(); + } + } + default: + return false; + } + return false; +} + ir::LabelledStatement *ParserImpl::ParseLabelledStatement(const lexer::LexerPosition &pos) { + const auto savedPos = lexer_->Save(); + bool isInIterator = isIterationStatement(); + lexer_->Rewind(savedPos); + const util::StringView &actualLabel = pos.token.Ident(); // TODO(frobert) : check correctness @@ -1356,7 +1462,11 @@ ir::LabelledStatement *ParserImpl::ParseLabelledStatement(const lexer::LexerPosi ThrowSyntaxError("Label already declared", pos.token.Start()); } - SavedParserContext newCtx(this, ParserStatus::IN_LABELED, actualLabel); + ParserStatus newStatus = ParserStatus::IN_LABELED; + if (isInIterator) { + newStatus |= ParserStatus::IN_ITERATION; + } + SavedParserContext newCtx(this, newStatus, actualLabel); auto *identNode = AllocNode(actualLabel, Allocator()); identNode->SetRange(pos.token.Loc());