2021-06-03 05:46:30 -03:00
/*
* Copyright ( c ) 2021 , Andreas Kling < kling @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2021-06-07 10:17:37 -03:00
# include <AK/Debug.h>
2021-06-09 05:02:01 -03:00
# include <AK/TemporaryChange.h>
2021-06-08 23:19:58 -03:00
# include <LibJS/Bytecode/BasicBlock.h>
2021-06-03 05:46:30 -03:00
# include <LibJS/Bytecode/Instruction.h>
# include <LibJS/Bytecode/Interpreter.h>
2021-06-09 04:19:34 -03:00
# include <LibJS/Bytecode/Op.h>
2022-02-11 16:08:21 -03:00
# include <LibJS/Interpreter.h>
2021-07-01 07:24:46 -03:00
# include <LibJS/Runtime/GlobalEnvironment.h>
2021-06-03 13:26:13 -03:00
# include <LibJS/Runtime/GlobalObject.h>
2021-09-11 16:27:36 -03:00
# include <LibJS/Runtime/Realm.h>
2021-06-03 05:46:30 -03:00
namespace JS : : Bytecode {
2021-06-05 10:53:36 -03:00
static Interpreter * s_current ;
2021-10-24 08:34:46 -03:00
bool g_dump_bytecode = false ;
2021-06-05 10:53:36 -03:00
Interpreter * Interpreter : : current ( )
{
return s_current ;
}
2021-09-11 15:36:25 -03:00
Interpreter : : Interpreter ( GlobalObject & global_object , Realm & realm )
2021-06-03 13:26:13 -03:00
: m_vm ( global_object . vm ( ) )
, m_global_object ( global_object )
2021-09-11 15:36:25 -03:00
, m_realm ( realm )
2021-06-03 05:46:30 -03:00
{
2021-06-05 10:53:36 -03:00
VERIFY ( ! s_current ) ;
s_current = this ;
2021-06-03 05:46:30 -03:00
}
Interpreter : : ~ Interpreter ( )
{
2021-06-05 10:53:36 -03:00
VERIFY ( s_current = = this ) ;
s_current = nullptr ;
2021-06-03 05:46:30 -03:00
}
2022-04-15 12:50:51 -03:00
Interpreter : : ValueAndFrame Interpreter : : run_and_return_frame ( Executable const & executable , BasicBlock const * entry_point , RegisterWindow * in_frame )
2021-06-03 05:46:30 -03:00
{
2021-06-09 04:11:20 -03:00
dbgln_if ( JS_BYTECODE_DEBUG , " Bytecode::Interpreter will run unit {:p} " , & executable ) ;
2021-06-03 05:46:30 -03:00
2021-06-09 05:02:01 -03:00
TemporaryChange restore_executable { m_current_executable , & executable } ;
2022-02-07 10:36:45 -03:00
VERIFY ( m_saved_exception . is_null ( ) ) ;
2021-06-09 05:02:01 -03:00
2022-02-07 13:12:09 -03:00
bool pushed_execution_context = false ;
2021-08-09 18:09:48 -03:00
ExecutionContext execution_context ( vm ( ) . heap ( ) ) ;
2022-01-22 09:03:06 -03: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.
2021-06-24 14:17:45 -03:00
execution_context . this_value = & global_object ( ) ;
2021-06-05 10:53:36 -03:00
static FlyString global_execution_context_name = " (*BC* global execution context) " ;
2021-06-24 14:17:45 -03:00
execution_context . function_name = global_execution_context_name ;
2021-09-11 16:27:36 -03:00
execution_context . lexical_environment = & m_realm . global_environment ( ) ;
execution_context . variable_environment = & m_realm . global_environment ( ) ;
2021-09-13 17:05:53 -03:00
execution_context . realm = & m_realm ;
2022-07-17 14:56:36 -03:00
execution_context . is_strict_mode = executable . is_strict_mode ;
2022-03-17 19:40:17 -03:00
vm ( ) . push_execution_context ( execution_context ) ;
2022-02-07 13:12:09 -03:00
pushed_execution_context = true ;
2021-06-05 10:53:36 -03:00
}
2021-06-05 10:11:00 -03:00
2021-06-10 18:08:30 -03:00
auto block = entry_point ? : & executable . basic_blocks . first ( ) ;
2022-04-15 12:50:51 -03:00
if ( in_frame )
m_register_windows . append ( in_frame ) ;
else
2022-02-12 13:18:45 -03:00
m_register_windows . append ( make < RegisterWindow > ( MarkedVector < Value > ( vm ( ) . heap ( ) ) , MarkedVector < Environment * > ( vm ( ) . heap ( ) ) , MarkedVector < Environment * > ( vm ( ) . heap ( ) ) ) ) ;
2021-06-08 23:19:58 -03:00
2021-11-10 18:04:44 -03:00
registers ( ) . resize ( executable . number_of_registers ) ;
registers ( ) [ Register : : global_object_index ] = Value ( & global_object ( ) ) ;
2021-06-08 23:19:58 -03:00
for ( ; ; ) {
Bytecode : : InstructionStreamIterator pc ( block - > instruction_stream ( ) ) ;
bool will_jump = false ;
bool will_return = false ;
while ( ! pc . at_end ( ) ) {
auto & instruction = * pc ;
2022-02-07 10:36:45 -03: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 ) ;
2021-06-10 10:04:38 -03:00
if ( m_unwind_contexts . is_empty ( ) )
break ;
auto & unwind_context = m_unwind_contexts . last ( ) ;
2021-10-25 07:30:54 -03:00
if ( unwind_context . executable ! = m_current_executable )
break ;
2021-06-10 10:04:38 -03:00
if ( unwind_context . handler ) {
block = unwind_context . handler ;
unwind_context . handler = nullptr ;
2022-03-13 23:20:50 -03:00
// If there's no finalizer, there's nowhere for the handler block to unwind to, so the unwind context is no longer needed.
if ( ! unwind_context . finalizer )
m_unwind_contexts . take_last ( ) ;
2022-02-07 10:36:45 -03:00
accumulator ( ) = exception_value ;
m_saved_exception = { } ;
2021-06-10 10:04:38 -03:00
will_jump = true ;
2021-10-24 18:52:39 -03:00
break ;
}
if ( unwind_context . finalizer ) {
2021-06-10 10:04:38 -03:00
block = unwind_context . finalizer ;
m_unwind_contexts . take_last ( ) ;
will_jump = true ;
2021-10-24 18:52:39 -03:00
break ;
2021-06-10 10:04:38 -03:00
}
2022-03-13 23:20:50 -03: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 10:04:38 -03:00
}
2021-06-08 23:19:58 -03:00
if ( m_pending_jump . has_value ( ) ) {
block = m_pending_jump . release_value ( ) ;
will_jump = true ;
break ;
}
if ( ! m_return_value . is_empty ( ) ) {
will_return = true ;
break ;
}
+ + pc ;
2021-06-04 07:07:38 -03:00
}
2021-06-08 23:19:58 -03:00
if ( will_return )
break ;
if ( pc . at_end ( ) & & ! will_jump )
2021-06-05 10:53:36 -03:00
break ;
2021-06-09 13:19:11 -03:00
2022-02-07 10:36:45 -03:00
if ( ! m_saved_exception . is_null ( ) )
2021-06-09 13:19:11 -03:00
break ;
2021-06-04 07:07:38 -03:00
}
2021-06-03 05:46:30 -03:00
2021-06-09 04:11:20 -03:00
dbgln_if ( JS_BYTECODE_DEBUG , " Bytecode::Interpreter did run unit {:p} " , & executable ) ;
2021-06-07 10:17:37 -03:00
if constexpr ( JS_BYTECODE_DEBUG ) {
for ( size_t i = 0 ; i < registers ( ) . size ( ) ; + + i ) {
String value_string ;
if ( registers ( ) [ i ] . is_empty ( ) )
value_string = " (empty) " ;
else
value_string = registers ( ) [ i ] . to_string_without_side_effects ( ) ;
dbgln ( " [{:3}] {} " , i , value_string ) ;
}
2021-06-03 05:46:30 -03:00
}
2021-06-05 10:53:36 -03:00
2022-04-15 12:50:51 -03:00
auto frame = m_register_windows . take_last ( ) ;
2021-06-05 10:53:36 -03:00
2021-06-07 08:57:02 -03:00
auto return_value = m_return_value . value_or ( js_undefined ( ) ) ;
m_return_value = { } ;
2021-06-05 10:53:36 -03: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 12:50:51 -03:00
window ( ) . registers [ 0 ] = return_value ;
2021-06-05 10:53:36 -03:00
2021-11-10 18:14:56 -03:00
// 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 13:12:09 -03:00
if ( pushed_execution_context ) {
VERIFY ( & vm ( ) . running_execution_context ( ) = = & execution_context ) ;
2021-06-24 14:17:45 -03:00
vm ( ) . pop_execution_context ( ) ;
2022-02-07 13:12:09 -03:00
}
2021-06-05 10:53:36 -03:00
2021-06-12 11:32:54 -03:00
vm ( ) . finish_execution_generation ( ) ;
2022-02-07 10:36:45 -03:00
if ( ! m_saved_exception . is_null ( ) ) {
Value thrown_value = m_saved_exception . value ( ) ;
m_saved_exception = { } ;
2022-04-15 12:50:51 -03:00
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 10:36:45 -03:00
}
2021-11-10 21:41:56 -03:00
2022-04-15 12:50:51 -03:00
if ( auto register_window = frame . get_pointer < NonnullOwnPtr < RegisterWindow > > ( ) )
return { return_value , move ( * register_window ) } ;
return { return_value , nullptr } ;
2021-06-03 05:46:30 -03:00
}
2021-06-10 10:04:38 -03:00
void Interpreter : : enter_unwind_context ( Optional < Label > handler_target , Optional < Label > finalizer_target )
{
2021-10-25 07:30:54 -03:00
m_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 10:04:38 -03:00
}
void Interpreter : : leave_unwind_context ( )
{
m_unwind_contexts . take_last ( ) ;
}
2022-02-07 10:36:45 -03:00
ThrowCompletionOr < void > Interpreter : : continue_pending_unwind ( Label const & resume_label )
2021-06-10 10:04:38 -03:00
{
if ( ! m_saved_exception . is_null ( ) ) {
2022-02-07 10:36:45 -03:00
auto result = throw_completion ( m_saved_exception . value ( ) ) ;
2021-06-10 10:04:38 -03:00
m_saved_exception = { } ;
2022-02-07 10:36:45 -03:00
return result ;
2021-06-10 10:04:38 -03:00
}
2022-02-07 10:36:45 -03:00
jump ( resume_label ) ;
return { } ;
2021-06-10 10:04:38 -03:00
}
2021-06-13 13:10:20 -03:00
2022-02-11 16:08:21 -03:00
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 13:10:20 -03:00
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 > ( ) ;
if ( level = = OptimizationLevel : : Default ) {
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 > ( ) ;
} else {
VERIFY_NOT_REACHED ( ) ;
}
auto & passes = * pm ;
entry = move ( pm ) ;
return passes ;
}
2021-06-03 05:46:30 -03:00
}