2021-06-03 10:46:30 +02:00
/*
* Copyright ( c ) 2021 , Andreas Kling < kling @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2021-06-07 15:17:37 +02:00
# include <AK/Debug.h>
2021-06-09 10:02:01 +02:00
# include <AK/TemporaryChange.h>
2021-06-09 06:49:58 +04:30
# include <LibJS/Bytecode/BasicBlock.h>
2021-06-03 10:46:30 +02:00
# include <LibJS/Bytecode/Instruction.h>
# include <LibJS/Bytecode/Interpreter.h>
2021-06-09 09:19:34 +02:00
# include <LibJS/Bytecode/Op.h>
2022-02-11 22:38:21 +03:30
# include <LibJS/Interpreter.h>
2021-07-01 12:24:46 +02:00
# include <LibJS/Runtime/GlobalEnvironment.h>
2021-06-03 18:26:13 +02:00
# include <LibJS/Runtime/GlobalObject.h>
2021-09-11 20:27:36 +01:00
# include <LibJS/Runtime/Realm.h>
2021-06-03 10:46:30 +02:00
namespace JS : : Bytecode {
2021-06-05 15:53:36 +02:00
static Interpreter * s_current ;
2021-10-24 13:34:46 +02:00
bool g_dump_bytecode = false ;
2021-06-05 15:53:36 +02:00
Interpreter * Interpreter : : current ( )
{
return s_current ;
}
2022-08-22 19:35:23 +01:00
Interpreter : : Interpreter ( Realm & realm )
: m_vm ( realm . vm ( ) )
2021-09-11 19:36:25 +01:00
, m_realm ( realm )
2021-06-03 10:46:30 +02:00
{
2021-06-05 15:53:36 +02:00
VERIFY ( ! s_current ) ;
s_current = this ;
2021-06-03 10:46:30 +02:00
}
Interpreter : : ~ Interpreter ( )
{
2021-06-05 15:53:36 +02:00
VERIFY ( s_current = = this ) ;
s_current = nullptr ;
2021-06-03 10:46:30 +02:00
}
2022-04-15 20:20:51 +04:30
Interpreter : : ValueAndFrame Interpreter : : run_and_return_frame ( Executable const & executable , BasicBlock const * entry_point , RegisterWindow * in_frame )
2021-06-03 10:46:30 +02:00
{
2021-06-09 09:11:20 +02:00
dbgln_if ( JS_BYTECODE_DEBUG , " Bytecode::Interpreter will run unit {:p} " , & executable ) ;
2021-06-03 10:46:30 +02:00
2021-06-09 10:02:01 +02:00
TemporaryChange restore_executable { m_current_executable , & executable } ;
2022-02-07 14:36:45 +01:00
VERIFY ( m_saved_exception . is_null ( ) ) ;
2021-06-09 10:02:01 +02:00
2022-02-07 17:12:09 +01:00
bool pushed_execution_context = false ;
2021-08-09 17:09:48 -04:00
ExecutionContext execution_context ( vm ( ) . heap ( ) ) ;
2022-01-22 13:03:06 +01:00
if ( vm ( ) . execution_context_stack ( ) . is_empty ( ) | | ! vm ( ) . running_execution_context ( ) . lexical_environment ) {
// The "normal" interpreter pushes an execution context without environment so in that case we also want to push one.
2022-08-22 19:35:23 +01:00
execution_context . this_value = & m_realm . global_object ( ) ;
2023-01-08 19:23:00 -05:00
static DeprecatedFlyString global_execution_context_name = " (*BC* global execution context) " ;
2021-06-24 19:17:45 +02:00
execution_context . function_name = global_execution_context_name ;
2021-09-11 20:27:36 +01:00
execution_context . lexical_environment = & m_realm . global_environment ( ) ;
execution_context . variable_environment = & m_realm . global_environment ( ) ;
2021-09-13 21:05:53 +01:00
execution_context . realm = & m_realm ;
2022-07-17 18:56:36 +01:00
execution_context . is_strict_mode = executable . is_strict_mode ;
2022-03-17 22:40:17 +00:00
vm ( ) . push_execution_context ( execution_context ) ;
2022-02-07 17:12:09 +01:00
pushed_execution_context = true ;
2021-06-05 15:53:36 +02:00
}
2021-06-05 15:11:00 +02:00
2022-11-08 19:38:57 +01:00
TemporaryChange restore_current_block { m_current_block , entry_point ? : & executable . basic_blocks . first ( ) } ;
2022-04-15 20:20:51 +04:30
if ( in_frame )
m_register_windows . append ( in_frame ) ;
else
2022-11-25 23:15:44 +00:00
m_register_windows . append ( make < RegisterWindow > ( MarkedVector < Value > ( vm ( ) . heap ( ) ) , MarkedVector < Environment * > ( vm ( ) . heap ( ) ) , MarkedVector < Environment * > ( vm ( ) . heap ( ) ) , Vector < UnwindInfo > { } ) ) ;
2021-06-09 06:49:58 +04:30
2021-11-11 00:34:44 +03:30
registers ( ) . resize ( executable . number_of_registers ) ;
2021-06-09 06:49:58 +04:30
for ( ; ; ) {
2022-10-30 11:58:46 +01:00
Bytecode : : InstructionStreamIterator pc ( m_current_block - > instruction_stream ( ) ) ;
TemporaryChange temp_change { m_pc , & pc } ;
2021-06-09 06:49:58 +04:30
bool will_jump = false ;
bool will_return = false ;
while ( ! pc . at_end ( ) ) {
auto & instruction = * pc ;
2022-02-07 14:36:45 +01:00
auto ran_or_error = instruction . execute ( * this ) ;
if ( ran_or_error . is_error ( ) ) {
auto exception_value = * ran_or_error . throw_completion ( ) . value ( ) ;
m_saved_exception = make_handle ( exception_value ) ;
2022-11-25 23:15:44 +00:00
if ( unwind_contexts ( ) . is_empty ( ) )
2021-06-10 15:04:38 +02:00
break ;
2022-11-25 23:15:44 +00:00
auto & unwind_context = unwind_contexts ( ) . last ( ) ;
2021-10-25 12:30:54 +02:00
if ( unwind_context . executable ! = m_current_executable )
break ;
2021-06-10 15:04:38 +02:00
if ( unwind_context . handler ) {
2022-10-30 11:58:46 +01:00
m_current_block = unwind_context . handler ;
2021-06-10 15:04:38 +02:00
unwind_context . handler = nullptr ;
2022-03-14 02:20:50 +00:00
2022-02-07 14:36:45 +01:00
accumulator ( ) = exception_value ;
m_saved_exception = { } ;
2021-06-10 15:04:38 +02:00
will_jump = true ;
2021-10-24 23:52:39 +02:00
break ;
}
if ( unwind_context . finalizer ) {
2022-10-30 11:58:46 +01:00
m_current_block = unwind_context . finalizer ;
2021-06-10 15:04:38 +02:00
will_jump = true ;
2021-10-24 23:52:39 +02:00
break ;
2021-06-10 15:04:38 +02:00
}
2022-03-14 02:20:50 +00:00
// An unwind context with no handler or finalizer? We have nowhere to jump, and continuing on will make us crash on the next `Call` to a non-native function if there's an exception! So let's crash here instead.
// If you run into this, you probably forgot to remove the current unwind_context somewhere.
VERIFY_NOT_REACHED ( ) ;
2021-06-10 15:04:38 +02:00
}
2021-06-09 06:49:58 +04:30
if ( m_pending_jump . has_value ( ) ) {
2022-10-30 11:58:46 +01:00
m_current_block = m_pending_jump . release_value ( ) ;
2021-06-09 06:49:58 +04:30
will_jump = true ;
break ;
}
if ( ! m_return_value . is_empty ( ) ) {
will_return = true ;
break ;
}
+ + pc ;
2021-06-04 12:07:38 +02:00
}
2021-06-09 06:49:58 +04:30
2022-11-13 20:56:53 +01:00
if ( will_jump )
continue ;
if ( ! unwind_contexts ( ) . is_empty ( ) ) {
auto & unwind_context = unwind_contexts ( ) . last ( ) ;
if ( unwind_context . executable = = m_current_executable & & unwind_context . finalizer ) {
m_saved_return_value = make_handle ( m_return_value ) ;
m_return_value = { } ;
m_current_block = unwind_context . finalizer ;
// the unwind_context will be pop'ed when entering the finally block
continue ;
}
}
2021-06-09 06:49:58 +04:30
2022-11-13 20:56:53 +01:00
if ( pc . at_end ( ) )
2021-06-05 15:53:36 +02:00
break ;
2021-06-09 18:19:11 +02:00
2022-02-07 14:36:45 +01:00
if ( ! m_saved_exception . is_null ( ) )
2021-06-09 18:19:11 +02:00
break ;
2022-11-13 20:56:53 +01:00
if ( will_return )
break ;
2021-06-04 12:07:38 +02:00
}
2021-06-03 10:46:30 +02:00
2021-06-09 09:11:20 +02:00
dbgln_if ( JS_BYTECODE_DEBUG , " Bytecode::Interpreter did run unit {:p} " , & executable ) ;
2021-06-07 15:17:37 +02:00
if constexpr ( JS_BYTECODE_DEBUG ) {
for ( size_t i = 0 ; i < registers ( ) . size ( ) ; + + i ) {
2022-12-04 18:02:33 +00:00
DeprecatedString value_string ;
2021-06-07 15:17:37 +02:00
if ( registers ( ) [ i ] . is_empty ( ) )
value_string = " (empty) " ;
else
2023-02-12 20:38:28 -05:00
value_string = registers ( ) [ i ] . to_deprecated_string_without_side_effects ( ) ;
2021-06-07 15:17:37 +02:00
dbgln ( " [{:3}] {} " , i , value_string ) ;
}
2021-06-03 10:46:30 +02:00
}
2021-06-05 15:53:36 +02:00
2022-04-15 20:20:51 +04:30
auto frame = m_register_windows . take_last ( ) ;
2021-06-05 15:53:36 +02:00
2022-11-13 20:56:53 +01:00
Value return_value = js_undefined ( ) ;
if ( ! m_return_value . is_empty ( ) ) {
return_value = m_return_value ;
m_return_value = { } ;
} else if ( ! m_saved_return_value . is_null ( ) ) {
return_value = m_saved_return_value . value ( ) ;
m_saved_return_value = { } ;
}
2021-06-05 15:53:36 +02:00
// NOTE: The return value from a called function is put into $0 in the caller context.
if ( ! m_register_windows . is_empty ( ) )
2022-04-15 20:20:51 +04:30
window ( ) . registers [ 0 ] = return_value ;
2021-06-05 15:53:36 +02:00
2021-11-11 00:44:56 +03:30
// At this point we may have already run any queued promise jobs via on_call_stack_emptied,
// in which case this is a no-op.
vm ( ) . run_queued_promise_jobs ( ) ;
2022-02-07 17:12:09 +01:00
if ( pushed_execution_context ) {
VERIFY ( & vm ( ) . running_execution_context ( ) = = & execution_context ) ;
2021-06-24 19:17:45 +02:00
vm ( ) . pop_execution_context ( ) ;
2022-02-07 17:12:09 +01:00
}
2021-06-05 15:53:36 +02:00
2021-06-12 17:32:54 +03:00
vm ( ) . finish_execution_generation ( ) ;
2022-02-07 14:36:45 +01:00
if ( ! m_saved_exception . is_null ( ) ) {
Value thrown_value = m_saved_exception . value ( ) ;
m_saved_exception = { } ;
2022-04-15 20:20:51 +04:30
if ( auto * register_window = frame . get_pointer < NonnullOwnPtr < RegisterWindow > > ( ) )
return { throw_completion ( thrown_value ) , move ( * register_window ) } ;
return { throw_completion ( thrown_value ) , nullptr } ;
2022-02-07 14:36:45 +01:00
}
2021-11-11 04:11:56 +03:30
2022-11-13 20:56:53 +01:00
if ( auto * register_window = frame . get_pointer < NonnullOwnPtr < RegisterWindow > > ( ) )
2022-04-15 20:20:51 +04:30
return { return_value , move ( * register_window ) } ;
return { return_value , nullptr } ;
2021-06-03 10:46:30 +02:00
}
2021-06-10 15:04:38 +02:00
void Interpreter : : enter_unwind_context ( Optional < Label > handler_target , Optional < Label > finalizer_target )
{
2022-11-25 23:15:44 +00:00
unwind_contexts ( ) . empend ( m_current_executable , handler_target . has_value ( ) ? & handler_target - > block ( ) : nullptr , finalizer_target . has_value ( ) ? & finalizer_target - > block ( ) : nullptr ) ;
2021-06-10 15:04:38 +02:00
}
void Interpreter : : leave_unwind_context ( )
{
2022-11-25 23:15:44 +00:00
unwind_contexts ( ) . take_last ( ) ;
2021-06-10 15:04:38 +02:00
}
2022-02-07 14:36:45 +01:00
ThrowCompletionOr < void > Interpreter : : continue_pending_unwind ( Label const & resume_label )
2021-06-10 15:04:38 +02:00
{
if ( ! m_saved_exception . is_null ( ) ) {
2022-02-07 14:36:45 +01:00
auto result = throw_completion ( m_saved_exception . value ( ) ) ;
2021-06-10 15:04:38 +02:00
m_saved_exception = { } ;
2022-02-07 14:36:45 +01:00
return result ;
2021-06-10 15:04:38 +02:00
}
2022-02-07 14:36:45 +01:00
2022-11-13 20:56:53 +01:00
if ( ! m_saved_return_value . is_null ( ) ) {
do_return ( m_saved_return_value . value ( ) ) ;
m_saved_return_value = { } ;
return { } ;
}
2022-02-07 14:36:45 +01:00
jump ( resume_label ) ;
return { } ;
2021-06-10 15:04:38 +02:00
}
2021-06-13 20:40:20 +04:30
2022-02-11 22:38:21 +03:30
VM : : InterpreterExecutionScope Interpreter : : ast_interpreter_scope ( )
{
if ( ! m_ast_interpreter )
m_ast_interpreter = JS : : Interpreter : : create_with_existing_realm ( m_realm ) ;
return { * m_ast_interpreter } ;
}
2021-06-13 20:40:20 +04:30
AK : : Array < OwnPtr < PassManager > , static_cast < UnderlyingType < Interpreter : : OptimizationLevel > > ( Interpreter : : OptimizationLevel : : __Count ) > Interpreter : : s_optimization_pipelines { } ;
Bytecode : : PassManager & Interpreter : : optimization_pipeline ( Interpreter : : OptimizationLevel level )
{
auto underlying_level = to_underlying ( level ) ;
VERIFY ( underlying_level < = to_underlying ( Interpreter : : OptimizationLevel : : __Count ) ) ;
auto & entry = s_optimization_pipelines [ underlying_level ] ;
if ( entry )
return * entry ;
auto pm = make < PassManager > ( ) ;
2022-10-19 14:35:26 +02:00
if ( level = = OptimizationLevel : : None ) {
// No optimization.
} else if ( level = = OptimizationLevel : : Optimize ) {
2021-06-13 20:40:20 +04:30
pm - > add < Passes : : GenerateCFG > ( ) ;
pm - > add < Passes : : UnifySameBlocks > ( ) ;
pm - > add < Passes : : GenerateCFG > ( ) ;
pm - > add < Passes : : MergeBlocks > ( ) ;
pm - > add < Passes : : GenerateCFG > ( ) ;
pm - > add < Passes : : UnifySameBlocks > ( ) ;
pm - > add < Passes : : GenerateCFG > ( ) ;
pm - > add < Passes : : MergeBlocks > ( ) ;
pm - > add < Passes : : GenerateCFG > ( ) ;
pm - > add < Passes : : PlaceBlocks > ( ) ;
2022-10-22 20:23:47 +02:00
pm - > add < Passes : : EliminateLoads > ( ) ;
2021-06-13 20:40:20 +04:30
} else {
VERIFY_NOT_REACHED ( ) ;
}
auto & passes = * pm ;
entry = move ( pm ) ;
return passes ;
}
2021-06-03 10:46:30 +02:00
}