mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 13:32:45 +00:00 
			
		
		
		
	LibJS: break or continue with nonexistent label is a syntax error
This commit is contained in:
		
							parent
							
								
									67f2301150
								
							
						
					
					
						commit
						e8da5f99b1
					
				
					 4 changed files with 32 additions and 5 deletions
				
			
		|  | @ -393,6 +393,11 @@ RefPtr<FunctionExpression> Parser::try_parse_arrow_function_expression(bool expe | |||
|     if (function_length == -1) | ||||
|         function_length = parameters.size(); | ||||
| 
 | ||||
|     auto old_labels_in_scope = move(m_parser_state.m_labels_in_scope); | ||||
|     ScopeGuard guard([&]() { | ||||
|         m_parser_state.m_labels_in_scope = move(old_labels_in_scope); | ||||
|     }); | ||||
| 
 | ||||
|     bool is_strict = false; | ||||
| 
 | ||||
|     auto function_body_result = [&]() -> RefPtr<BlockStatement> { | ||||
|  | @ -440,7 +445,9 @@ RefPtr<Statement> Parser::try_parse_labelled_statement() | |||
| 
 | ||||
|     if (!match_statement()) | ||||
|         return {}; | ||||
|     m_parser_state.m_labels_in_scope.set(identifier); | ||||
|     auto statement = parse_statement(); | ||||
|     m_parser_state.m_labels_in_scope.remove(identifier); | ||||
| 
 | ||||
|     statement->set_label(identifier); | ||||
|     state_rollback_guard.disarm(); | ||||
|  | @ -1318,8 +1325,13 @@ NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(bool check_for_funct | |||
|     if (function_length == -1) | ||||
|         function_length = parameters.size(); | ||||
| 
 | ||||
|     bool is_strict = false; | ||||
|     TemporaryChange change(m_parser_state.m_in_function_context, true); | ||||
|     auto old_labels_in_scope = move(m_parser_state.m_labels_in_scope); | ||||
|     ScopeGuard guard([&]() { | ||||
|         m_parser_state.m_labels_in_scope = move(old_labels_in_scope); | ||||
|     }); | ||||
| 
 | ||||
|     bool is_strict = false; | ||||
|     auto body = parse_block_statement(is_strict); | ||||
|     body->add_variables(m_parser_state.m_var_scopes.last()); | ||||
|     body->add_functions(m_parser_state.m_function_scopes.last()); | ||||
|  | @ -1395,8 +1407,11 @@ NonnullRefPtr<BreakStatement> Parser::parse_break_statement() | |||
|     if (match(TokenType::Semicolon)) { | ||||
|         consume(); | ||||
|     } else { | ||||
|         if (match(TokenType::Identifier) && !m_parser_state.m_current_token.trivia().contains('\n')) | ||||
|         if (match(TokenType::Identifier) && !m_parser_state.m_current_token.trivia().contains('\n')) { | ||||
|             target_label = consume().value(); | ||||
|             if (!m_parser_state.m_labels_in_scope.contains(target_label)) | ||||
|                 syntax_error(String::formatted("Label '{}' not found", target_label)); | ||||
|         } | ||||
|         consume_or_insert_semicolon(); | ||||
|     } | ||||
| 
 | ||||
|  | @ -1417,8 +1432,11 @@ NonnullRefPtr<ContinueStatement> Parser::parse_continue_statement() | |||
|         consume(); | ||||
|         return create_ast_node<ContinueStatement>(target_label); | ||||
|     } | ||||
|     if (match(TokenType::Identifier) && !m_parser_state.m_current_token.trivia().contains('\n')) | ||||
|     if (match(TokenType::Identifier) && !m_parser_state.m_current_token.trivia().contains('\n')) { | ||||
|         target_label = consume().value(); | ||||
|         if (!m_parser_state.m_labels_in_scope.contains(target_label)) | ||||
|             syntax_error(String::formatted("Label '{}' not found", target_label)); | ||||
|     } | ||||
|     consume_or_insert_semicolon(); | ||||
|     return create_ast_node<ContinueStatement>(target_label); | ||||
| } | ||||
|  |  | |||
|  | @ -26,6 +26,7 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <AK/HashTable.h> | ||||
| #include <AK/NonnullRefPtr.h> | ||||
| #include <AK/StringBuilder.h> | ||||
| #include <LibJS/AST.h> | ||||
|  | @ -155,6 +156,7 @@ private: | |||
|         Vector<NonnullRefPtrVector<VariableDeclaration>> m_let_scopes; | ||||
|         Vector<NonnullRefPtrVector<FunctionDeclaration>> m_function_scopes; | ||||
|         UseStrictDirectiveState m_use_strict_directive { UseStrictDirectiveState::None }; | ||||
|         HashTable<StringView> m_labels_in_scope; | ||||
|         bool m_strict_mode { false }; | ||||
|         bool m_allow_super_property_lookup { false }; | ||||
|         bool m_allow_super_constructor_call { false }; | ||||
|  |  | |||
|  | @ -2,8 +2,7 @@ test("'break' syntax errors", () => { | |||
|     expect("break").not.toEval(); | ||||
|     expect("break label").not.toEval(); | ||||
|     expect("{ break }").not.toEval(); | ||||
|     // FIXME: Parser does not throw error on nonexistent label
 | ||||
|     // expect("{ break label }.not.toEval();
 | ||||
|     expect("{ break label }").not.toEval(); | ||||
|     expect("label: { break label }").toEval(); | ||||
| }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -37,3 +37,11 @@ test("labeled for loop with continue", () => { | |||
|     } | ||||
|     expect(counter).toBe(6); | ||||
| }); | ||||
| 
 | ||||
| test("invalid label across scope", () => { | ||||
|     expect(` | ||||
|         label: { | ||||
|             (() => { break label; }); | ||||
|         } | ||||
|     `).not.toEval();
 | ||||
| }); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Matthew Olsson
						Matthew Olsson