mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 06:12:43 +00:00 
			
		
		
		
	LibJS: Generate unwind chains for break in Bytecode
This uses a newly added instruction `ScheduleJump` This instruction tells the finally proceeding it, that instead of jumping to it's next block it should jump to the designated block.
This commit is contained in:
		
							parent
							
								
									495ba53e46
								
							
						
					
					
						commit
						f5376cb282
					
				
					 9 changed files with 123 additions and 19 deletions
				
			
		|  | @ -262,23 +262,62 @@ CodeGenerationErrorOr<void> Generator::emit_delete_reference(JS::ASTNode const& | |||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| Label Generator::perform_needed_unwinds_for_labelled_break_and_return_target_block(DeprecatedFlyString const& break_label) | ||||
| void Generator::generate_break() | ||||
| { | ||||
|     bool last_was_finally = false; | ||||
|     // FIXME: Reduce code duplication
 | ||||
|     for (size_t i = m_boundaries.size(); i > 0; --i) { | ||||
|         auto boundary = m_boundaries[i - 1]; | ||||
|         using enum BlockBoundaryType; | ||||
|         switch (boundary) { | ||||
|         case Break: | ||||
|             emit<Op::Jump>().set_targets(nearest_breakable_scope(), {}); | ||||
|             return; | ||||
|         case Unwind: | ||||
|             if (!last_was_finally) | ||||
|                 emit<Bytecode::Op::LeaveUnwindContext>(); | ||||
|             last_was_finally = false; | ||||
|             break; | ||||
|         case LeaveLexicalEnvironment: | ||||
|             emit<Bytecode::Op::LeaveEnvironment>(Bytecode::Op::EnvironmentMode::Lexical); | ||||
|             break; | ||||
|         case LeaveVariableEnvironment: | ||||
|             emit<Bytecode::Op::LeaveEnvironment>(Bytecode::Op::EnvironmentMode::Var); | ||||
|             break; | ||||
|         case Continue: | ||||
|             break; | ||||
|         case ReturnToFinally: { | ||||
|             auto& block = make_block(DeprecatedString::formatted("{}.break", current_block().name())); | ||||
|             emit<Op::ScheduleJump>(Label { block }); | ||||
|             switch_to_basic_block(block); | ||||
|             last_was_finally = true; | ||||
|             break; | ||||
|         }; | ||||
|         } | ||||
|     } | ||||
|     VERIFY_NOT_REACHED(); | ||||
| } | ||||
| 
 | ||||
| void Generator::generate_break(DeprecatedFlyString const& break_label) | ||||
| { | ||||
|     size_t current_boundary = m_boundaries.size(); | ||||
|     for (auto& breakable_scope : m_breakable_scopes.in_reverse()) { | ||||
|     bool last_was_finally = false; | ||||
|     for (auto const& breakable_scope : m_breakable_scopes.in_reverse()) { | ||||
|         for (; current_boundary > 0; --current_boundary) { | ||||
|             auto boundary = m_boundaries[current_boundary - 1]; | ||||
|             // FIXME: Handle ReturnToFinally in a graceful manner
 | ||||
|             //        We need to execute the finally block, but tell it to resume
 | ||||
|             //        execution at the designated label
 | ||||
|             if (boundary == BlockBoundaryType::Unwind) { | ||||
|                 emit<Bytecode::Op::LeaveUnwindContext>(); | ||||
|                 if (!last_was_finally) | ||||
|                     emit<Bytecode::Op::LeaveUnwindContext>(); | ||||
|                 last_was_finally = false; | ||||
|             } else if (boundary == BlockBoundaryType::LeaveLexicalEnvironment) { | ||||
|                 emit<Bytecode::Op::LeaveEnvironment>(Bytecode::Op::EnvironmentMode::Lexical); | ||||
|             } else if (boundary == BlockBoundaryType::LeaveVariableEnvironment) { | ||||
|                 emit<Bytecode::Op::LeaveEnvironment>(Bytecode::Op::EnvironmentMode::Var); | ||||
|             } else if (boundary == BlockBoundaryType::ReturnToFinally) { | ||||
|                 // FIXME: We need to enter the `finally`, while still scheduling the break to happen
 | ||||
|                 auto& block = make_block(DeprecatedString::formatted("{}.break", current_block().name())); | ||||
|                 emit<Op::ScheduleJump>(Label { block }); | ||||
|                 switch_to_basic_block(block); | ||||
|                 last_was_finally = true; | ||||
|             } else if (boundary == BlockBoundaryType::Break) { | ||||
|                 // Make sure we don't process this boundary twice if the current breakable scope doesn't contain the target label.
 | ||||
|                 --current_boundary; | ||||
|  | @ -286,8 +325,10 @@ Label Generator::perform_needed_unwinds_for_labelled_break_and_return_target_blo | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (breakable_scope.language_label_set.contains_slow(break_label)) | ||||
|             return breakable_scope.bytecode_target; | ||||
|         if (breakable_scope.language_label_set.contains_slow(break_label)) { | ||||
|             emit<Op::Jump>().set_targets(breakable_scope.bytecode_target, {}); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // We must have a breakable scope available that contains the label, as this should be enforced by the parser.
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Hendiadyoin1
						Hendiadyoin1