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>
2023-09-27 10:10:00 +02:00
# include <AK/HashTable.h>
2021-06-09 10:02:01 +02:00
# include <AK/TemporaryChange.h>
2023-06-15 12:36:57 +02:00
# include <LibJS/AST.h>
2021-06-09 06:49:58 +04:30
# include <LibJS/Bytecode/BasicBlock.h>
2023-06-17 13:16:35 +02:00
# include <LibJS/Bytecode/Generator.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>
2023-10-14 14:37:48 +02:00
# include <LibJS/JIT/Compiler.h>
2023-09-27 10:10:00 +02:00
# include <LibJS/Runtime/AbstractOperations.h>
# include <LibJS/Runtime/Array.h>
# include <LibJS/Runtime/BigInt.h>
# include <LibJS/Runtime/DeclarativeEnvironment.h>
# include <LibJS/Runtime/ECMAScriptFunctionObject.h>
# include <LibJS/Runtime/Environment.h>
# include <LibJS/Runtime/FunctionEnvironment.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>
2023-09-27 10:10:00 +02:00
# include <LibJS/Runtime/Iterator.h>
# include <LibJS/Runtime/NativeFunction.h>
# include <LibJS/Runtime/ObjectEnvironment.h>
2021-09-11 20:27:36 +01:00
# include <LibJS/Runtime/Realm.h>
2023-09-27 10:10:00 +02:00
# include <LibJS/Runtime/Reference.h>
# include <LibJS/Runtime/RegExpObject.h>
# include <LibJS/Runtime/Value.h>
2023-10-06 17:54:21 +02:00
# include <LibJS/Runtime/ValueInlines.h>
2023-09-27 10:10:00 +02:00
# include <LibJS/SourceTextModule.h>
2021-06-03 10:46:30 +02:00
namespace JS : : Bytecode {
2021-10-24 13:34:46 +02:00
bool g_dump_bytecode = false ;
2021-06-05 15:53:36 +02:00
2023-06-22 15:59:18 +02:00
Interpreter : : Interpreter ( VM & vm )
: m_vm ( vm )
2021-06-05 15:53:36 +02:00
{
2021-06-03 10:46:30 +02:00
}
Interpreter : : ~ Interpreter ( )
{
}
2023-07-02 12:53:10 +02:00
void Interpreter : : visit_edges ( Cell : : Visitor & visitor )
{
2023-07-20 10:46:42 +02:00
for ( auto & frame : m_call_frames ) {
frame . visit ( [ & ] ( auto & value ) { value - > visit_edges ( visitor ) ; } ) ;
2023-07-02 12:53:10 +02:00
}
}
2023-06-15 12:36:57 +02:00
// 16.1.6 ScriptEvaluation ( scriptRecord ), https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation
ThrowCompletionOr < Value > Interpreter : : run ( Script & script_record , JS : : GCPtr < Environment > lexical_environment_override )
{
auto & vm = this - > vm ( ) ;
// 1. Let globalEnv be scriptRecord.[[Realm]].[[GlobalEnv]].
auto & global_environment = script_record . realm ( ) . global_environment ( ) ;
// 2. Let scriptContext be a new ECMAScript code execution context.
ExecutionContext script_context ( vm . heap ( ) ) ;
// 3. Set the Function of scriptContext to null.
// NOTE: This was done during execution context construction.
// 4. Set the Realm of scriptContext to scriptRecord.[[Realm]].
script_context . realm = & script_record . realm ( ) ;
// 5. Set the ScriptOrModule of scriptContext to scriptRecord.
script_context . script_or_module = NonnullGCPtr < Script > ( script_record ) ;
// 6. Set the VariableEnvironment of scriptContext to globalEnv.
script_context . variable_environment = & global_environment ;
// 7. Set the LexicalEnvironment of scriptContext to globalEnv.
script_context . lexical_environment = & global_environment ;
// Non-standard: Override the lexical environment if requested.
if ( lexical_environment_override )
script_context . lexical_environment = lexical_environment_override ;
// 8. Set the PrivateEnvironment of scriptContext to null.
// NOTE: This isn't in the spec, but we require it.
script_context . is_strict_mode = script_record . parse_node ( ) . is_strict_mode ( ) ;
// FIXME: 9. Suspend the currently running execution context.
// 10. Push scriptContext onto the execution context stack; scriptContext is now the running execution context.
TRY ( vm . push_execution_context ( script_context , { } ) ) ;
// 11. Let script be scriptRecord.[[ECMAScriptCode]].
auto & script = script_record . parse_node ( ) ;
// 12. Let result be Completion(GlobalDeclarationInstantiation(script, globalEnv)).
auto instantiation_result = script . global_declaration_instantiation ( vm , global_environment ) ;
Completion result = instantiation_result . is_throw_completion ( ) ? instantiation_result . throw_completion ( ) : normal_completion ( { } ) ;
// 13. If result.[[Type]] is normal, then
if ( result . type ( ) = = Completion : : Type : : Normal ) {
auto executable_result = JS : : Bytecode : : Generator : : generate ( script ) ;
if ( executable_result . is_error ( ) ) {
if ( auto error_string = executable_result . error ( ) . to_string ( ) ; error_string . is_error ( ) )
result = vm . template throw_completion < JS : : InternalError > ( vm . error_message ( JS : : VM : : ErrorMessage : : OutOfMemory ) ) ;
else if ( error_string = String : : formatted ( " TODO({}) " , error_string.value()) ; error_string . is_error ( ) )
result = vm . template throw_completion < JS : : InternalError > ( vm . error_message ( JS : : VM : : ErrorMessage : : OutOfMemory ) ) ;
else
result = JS : : throw_completion ( JS : : InternalError : : create ( realm ( ) , error_string . release_value ( ) ) ) ;
} else {
auto executable = executable_result . release_value ( ) ;
if ( g_dump_bytecode )
executable - > dump ( ) ;
// a. Set result to the result of evaluating script.
2023-09-21 09:26:32 +02:00
auto result_or_error = run_and_return_frame ( * executable , nullptr ) ;
2023-06-15 12:36:57 +02:00
if ( result_or_error . value . is_error ( ) )
result = result_or_error . value . release_error ( ) ;
else
result = result_or_error . frame - > registers [ 0 ] ;
}
}
// 14. If result.[[Type]] is normal and result.[[Value]] is empty, then
if ( result . type ( ) = = Completion : : Type : : Normal & & ! result . value ( ) . has_value ( ) ) {
// a. Set result to NormalCompletion(undefined).
result = normal_completion ( js_undefined ( ) ) ;
}
// FIXME: 15. Suspend scriptContext and remove it from the execution context stack.
vm . pop_execution_context ( ) ;
// 16. Assert: The execution context stack is not empty.
VERIFY ( ! vm . execution_context_stack ( ) . is_empty ( ) ) ;
// FIXME: 17. Resume the context that is now on the top of the execution context stack as the running execution context.
// 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.
// FIXME: These three should be moved out of Interpreter::run and give the host an option to run these, as it's up to the host when these get run.
// https://tc39.es/ecma262/#sec-jobs for jobs and https://tc39.es/ecma262/#_ref_3508 for ClearKeptObjects
// finish_execution_generation is particularly an issue for LibWeb, as the HTML spec wants to run it specifically after performing a microtask checkpoint.
// The promise and registry cleanup queues don't cause LibWeb an issue, as LibWeb overrides the hooks that push onto these queues.
vm . run_queued_promise_jobs ( ) ;
vm . run_queued_finalization_registry_cleanup_jobs ( ) ;
vm . finish_execution_generation ( ) ;
// 18. Return ? result.
if ( result . is_abrupt ( ) ) {
VERIFY ( result . type ( ) = = Completion : : Type : : Throw ) ;
return result . release_error ( ) ;
}
VERIFY ( result . value ( ) . has_value ( ) ) ;
return * result . value ( ) ;
}
ThrowCompletionOr < Value > Interpreter : : run ( SourceTextModule & module )
{
// FIXME: This is not a entry point as defined in the spec, but is convenient.
// To avoid work we use link_and_eval_module however that can already be
// dangerous if the vm loaded other modules.
auto & vm = this - > vm ( ) ;
TRY ( vm . link_and_eval_module ( Badge < Bytecode : : Interpreter > { } , module ) ) ;
vm . run_queued_promise_jobs ( ) ;
vm . run_queued_finalization_registry_cleanup_jobs ( ) ;
return js_undefined ( ) ;
}
2023-09-26 16:41:42 +02:00
void Interpreter : : run_bytecode ( )
2021-06-03 10:46:30 +02:00
{
2023-09-27 10:31:07 +02:00
auto * locals = vm ( ) . running_execution_context ( ) . local_variables . data ( ) ;
auto * registers = this - > registers ( ) . data ( ) ;
auto & accumulator = this - > accumulator ( ) ;
2021-06-09 06:49:58 +04:30
for ( ; ; ) {
2023-09-26 16:45:55 +02:00
start :
2023-09-01 16:53:55 +02:00
auto pc = InstructionStreamIterator { m_current_block - > instruction_stream ( ) , m_current_executable } ;
TemporaryChange temp_change { m_pc , Optional < InstructionStreamIterator & > ( pc ) } ;
2022-10-30 11:58:46 +01:00
2021-06-09 06:49:58 +04:30
bool will_return = false ;
2022-12-06 20:45:38 +01:00
bool will_yield = false ;
2023-09-26 16:41:42 +02:00
2023-09-26 19:43:44 +02:00
ThrowCompletionOr < void > result ;
2021-06-09 06:49:58 +04:30
while ( ! pc . at_end ( ) ) {
auto & instruction = * pc ;
2023-09-26 17:14:59 +02:00
2023-09-26 19:43:44 +02:00
switch ( instruction . type ( ) ) {
2023-09-27 10:31:07 +02:00
case Instruction : : Type : : GetLocal : {
auto & local = locals [ static_cast < Op : : GetLocal const & > ( instruction ) . index ( ) ] ;
if ( local . is_empty ( ) ) {
auto const & variable_name = vm ( ) . running_execution_context ( ) . function - > local_variables_names ( ) [ static_cast < Op : : GetLocal const & > ( instruction ) . index ( ) ] ;
result = vm ( ) . throw_completion < ReferenceError > ( ErrorType : : BindingNotInitialized , variable_name ) ;
break ;
}
accumulator = local ;
break ;
}
case Instruction : : Type : : SetLocal :
locals [ static_cast < Op : : SetLocal const & > ( instruction ) . index ( ) ] = accumulator ;
break ;
case Instruction : : Type : : Load :
accumulator = registers [ static_cast < Op : : Load const & > ( instruction ) . src ( ) . index ( ) ] ;
break ;
case Instruction : : Type : : Store :
registers [ static_cast < Op : : Store const & > ( instruction ) . dst ( ) . index ( ) ] = accumulator ;
break ;
case Instruction : : Type : : LoadImmediate :
accumulator = static_cast < Op : : LoadImmediate const & > ( instruction ) . value ( ) ;
break ;
2023-09-26 19:43:44 +02:00
case Instruction : : Type : : Jump :
m_current_block = & static_cast < Op : : Jump const & > ( instruction ) . true_target ( ) - > block ( ) ;
goto start ;
case Instruction : : Type : : JumpConditional :
2023-09-27 10:31:07 +02:00
if ( accumulator . to_boolean ( ) )
2023-09-26 19:43:44 +02:00
m_current_block = & static_cast < Op : : Jump const & > ( instruction ) . true_target ( ) - > block ( ) ;
else
m_current_block = & static_cast < Op : : Jump const & > ( instruction ) . false_target ( ) - > block ( ) ;
goto start ;
case Instruction : : Type : : JumpNullish :
2023-09-27 10:31:07 +02:00
if ( accumulator . is_nullish ( ) )
2023-09-26 19:43:44 +02:00
m_current_block = & static_cast < Op : : Jump const & > ( instruction ) . true_target ( ) - > block ( ) ;
else
m_current_block = & static_cast < Op : : Jump const & > ( instruction ) . false_target ( ) - > block ( ) ;
goto start ;
case Instruction : : Type : : JumpUndefined :
2023-09-27 10:31:07 +02:00
if ( accumulator . is_undefined ( ) )
2023-09-26 19:43:44 +02:00
m_current_block = & static_cast < Op : : Jump const & > ( instruction ) . true_target ( ) - > block ( ) ;
else
m_current_block = & static_cast < Op : : Jump const & > ( instruction ) . false_target ( ) - > block ( ) ;
goto start ;
case Instruction : : Type : : EnterUnwindContext :
enter_unwind_context (
static_cast < Op : : EnterUnwindContext const & > ( instruction ) . handler_target ( ) ,
static_cast < Op : : EnterUnwindContext const & > ( instruction ) . finalizer_target ( ) ) ;
m_current_block = & static_cast < Op : : EnterUnwindContext const & > ( instruction ) . entry_point ( ) . block ( ) ;
goto start ;
case Instruction : : Type : : ContinuePendingUnwind :
if ( auto exception = reg ( Register : : exception ( ) ) ; ! exception . is_empty ( ) ) {
result = throw_completion ( exception ) ;
break ;
}
if ( ! saved_return_value ( ) . is_empty ( ) ) {
do_return ( saved_return_value ( ) ) ;
break ;
}
if ( m_scheduled_jump ) {
// FIXME: If we `break` or `continue` in the finally, we need to clear
// this field
m_current_block = exchange ( m_scheduled_jump , nullptr ) ;
} else {
m_current_block = & static_cast < Op : : ContinuePendingUnwind const & > ( instruction ) . resume_target ( ) . block ( ) ;
}
goto start ;
case Instruction : : Type : : ScheduleJump :
m_scheduled_jump = & static_cast < Op : : ScheduleJump const & > ( instruction ) . target ( ) . block ( ) ;
m_current_block = unwind_contexts ( ) . last ( ) . finalizer ;
goto start ;
default :
result = instruction . execute ( * this ) ;
break ;
}
if ( result . is_error ( ) ) [[unlikely]] {
reg ( Register : : exception ( ) ) = * result . throw_completion ( ) . value ( ) ;
2022-11-25 23:15:44 +00:00
if ( unwind_contexts ( ) . is_empty ( ) )
2023-09-26 17:14:59 +02:00
return ;
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 )
2023-09-26 17:14:59 +02:00
return ;
2023-07-14 15:50:36 +02:00
if ( unwind_context . handler & & ! unwind_context . handler_called ) {
2023-05-13 18:53:14 +02:00
vm ( ) . running_execution_context ( ) . lexical_environment = unwind_context . lexical_environment ;
2022-10-30 11:58:46 +01:00
m_current_block = unwind_context . handler ;
2023-07-14 15:50:36 +02:00
unwind_context . handler_called = true ;
2022-03-14 02:20:50 +00:00
2023-09-27 10:31:07 +02:00
accumulator = reg ( Register : : exception ( ) ) ;
2023-09-26 11:57:42 +02:00
reg ( Register : : exception ( ) ) = { } ;
2023-09-26 16:45:55 +02:00
goto start ;
2021-10-24 23:52:39 +02:00
}
if ( unwind_context . finalizer ) {
2022-10-30 11:58:46 +01:00
m_current_block = unwind_context . finalizer ;
2023-07-14 15:50:36 +02:00
// If an exception was thrown inside the corresponding `catch` block, we need to rethrow it
// from the `finally` block. But if the exception is from the `try` block, and has already been
// handled by `catch`, we swallow it.
if ( ! unwind_context . handler_called )
2023-09-26 11:57:42 +02:00
reg ( Register : : exception ( ) ) = { } ;
2023-09-26 16:45:55 +02:00
goto start ;
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
}
2023-09-26 19:43:44 +02:00
2023-09-26 15:32:46 +02:00
if ( ! reg ( Register : : return_value ( ) ) . is_empty ( ) ) {
2021-06-09 06:49:58 +04:30
will_return = true ;
2022-12-06 20:45:38 +01:00
// Note: A `yield` statement will not go through a finally statement,
// hence we need to set a flag to not do so,
// but we generate a Yield Operation in the case of returns in
// generators as well, so we need to check if it will actually
// continue or is a `return` in disguise
2023-07-15 09:59:19 +02:00
will_yield = ( instruction . type ( ) = = Instruction : : Type : : Yield & & static_cast < Op : : Yield const & > ( instruction ) . continuation ( ) . has_value ( ) ) | | instruction . type ( ) = = Instruction : : Type : : Await ;
2021-06-09 06:49:58 +04:30
break ;
}
+ + pc ;
2021-06-04 12:07:38 +02:00
}
2021-06-09 06:49:58 +04:30
2022-12-06 20:45:38 +01:00
if ( ! unwind_contexts ( ) . is_empty ( ) & & ! will_yield ) {
2022-11-13 20:56:53 +01:00
auto & unwind_context = unwind_contexts ( ) . last ( ) ;
if ( unwind_context . executable = = m_current_executable & & unwind_context . finalizer ) {
2023-09-26 15:32:46 +02:00
reg ( Register : : saved_return_value ( ) ) = reg ( Register : : return_value ( ) ) ;
reg ( Register : : return_value ( ) ) = { } ;
2022-11-13 20:56:53 +01:00
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-11-13 20:56:53 +01:00
if ( will_return )
break ;
2021-06-04 12:07:38 +02:00
}
2023-09-26 16:41:42 +02:00
}
Interpreter : : ValueAndFrame Interpreter : : run_and_return_frame ( Executable & executable , BasicBlock const * entry_point , CallFrame * in_frame )
{
dbgln_if ( JS_BYTECODE_DEBUG , " Bytecode::Interpreter will run unit {:p} " , & executable ) ;
TemporaryChange restore_executable { m_current_executable , & executable } ;
TemporaryChange restore_saved_jump { m_scheduled_jump , static_cast < BasicBlock const * > ( nullptr ) } ;
VERIFY ( ! vm ( ) . execution_context_stack ( ) . is_empty ( ) ) ;
TemporaryChange restore_current_block { m_current_block , entry_point ? : executable . basic_blocks . first ( ) } ;
if ( in_frame )
push_call_frame ( in_frame , executable . number_of_registers ) ;
else
push_call_frame ( make < CallFrame > ( ) , executable . number_of_registers ) ;
2023-10-14 14:37:48 +02:00
if ( auto native_executable = JIT : : Compiler : : compile ( executable ) ) {
native_executable - > run ( vm ( ) ) ;
for ( size_t i = 0 ; i < vm ( ) . running_execution_context ( ) . local_variables . size ( ) ; + + i ) {
dbgln ( " %{}: {} " , i , vm ( ) . running_execution_context ( ) . local_variables [ i ] ) ;
}
} else {
run_bytecode ( ) ;
}
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 ) {
2023-02-12 21:54:02 -05:00
String value_string ;
2021-06-07 15:17:37 +02:00
if ( registers ( ) [ i ] . is_empty ( ) )
2023-08-07 11:12:38 +02:00
value_string = " (empty) " _string ;
2021-06-07 15:17:37 +02:00
else
2023-08-09 08:49:02 +02:00
value_string = registers ( ) [ i ] . to_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
2023-09-26 15:32:46 +02:00
auto return_value = js_undefined ( ) ;
if ( ! reg ( Register : : return_value ( ) ) . is_empty ( ) )
return_value = reg ( Register : : return_value ( ) ) ;
else if ( ! reg ( Register : : saved_return_value ( ) ) . is_empty ( ) )
return_value = reg ( Register : : saved_return_value ( ) ) ;
2023-09-26 11:57:42 +02:00
auto exception = reg ( Register : : exception ( ) ) ;
2023-07-21 17:30:07 +02:00
2023-07-20 10:46:42 +02:00
auto frame = pop_call_frame ( ) ;
2021-06-05 15:53:36 +02:00
// NOTE: The return value from a called function is put into $0 in the caller context.
2023-07-20 10:46:42 +02:00
if ( ! m_call_frames . is_empty ( ) )
call_frame ( ) . 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 ( ) ;
2021-06-12 17:32:54 +03:00
vm ( ) . finish_execution_generation ( ) ;
2023-09-26 11:57:42 +02:00
if ( ! exception . is_empty ( ) ) {
2023-07-20 10:46:42 +02:00
if ( auto * call_frame = frame . get_pointer < NonnullOwnPtr < CallFrame > > ( ) )
2023-09-26 11:57:42 +02:00
return { throw_completion ( exception ) , move ( * call_frame ) } ;
return { throw_completion ( exception ) , nullptr } ;
2022-02-07 14:36:45 +01:00
}
2021-11-11 04:11:56 +03:30
2023-07-20 10:46:42 +02:00
if ( auto * call_frame = frame . get_pointer < NonnullOwnPtr < CallFrame > > ( ) )
return { return_value , move ( * call_frame ) } ;
2022-04-15 20:20:51 +04:30
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 )
{
2023-05-13 18:53:14 +02:00
unwind_contexts ( ) . empend (
m_current_executable ,
handler_target . has_value ( ) ? & handler_target - > block ( ) : nullptr ,
finalizer_target . has_value ( ) ? & finalizer_target - > block ( ) : nullptr ,
2023-07-02 15:05:17 +02:00
vm ( ) . running_execution_context ( ) . lexical_environment ) ;
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
}
2023-10-03 08:18:10 +02:00
ThrowCompletionOr < NonnullRefPtr < Bytecode : : Executable > > compile ( VM & vm , ASTNode const & node , FunctionKind kind , DeprecatedFlyString const & name )
2023-06-22 15:59:18 +02:00
{
auto executable_result = Bytecode : : Generator : : generate ( node , kind ) ;
if ( executable_result . is_error ( ) )
return vm . throw_completion < InternalError > ( ErrorType : : NotImplemented , TRY_OR_THROW_OOM ( vm , executable_result . error ( ) . to_string ( ) ) ) ;
auto bytecode_executable = executable_result . release_value ( ) ;
bytecode_executable - > name = name ;
2023-06-26 20:10:11 +02:00
2023-06-22 15:59:18 +02:00
if ( Bytecode : : g_dump_bytecode )
bytecode_executable - > dump ( ) ;
return bytecode_executable ;
}
Realm & Interpreter : : realm ( )
{
return * m_vm . current_realm ( ) ;
}
2023-07-20 10:46:42 +02:00
void Interpreter : : push_call_frame ( Variant < NonnullOwnPtr < CallFrame > , CallFrame * > frame , size_t register_count )
2023-07-01 13:56:40 +02:00
{
2023-07-20 10:46:42 +02:00
m_call_frames . append ( move ( frame ) ) ;
this - > call_frame ( ) . registers . resize ( register_count ) ;
m_current_call_frame = this - > call_frame ( ) . registers ;
2023-09-26 15:32:46 +02:00
reg ( Register : : return_value ( ) ) = { } ;
2023-07-01 13:56:40 +02:00
}
2023-07-20 10:46:42 +02:00
Variant < NonnullOwnPtr < CallFrame > , CallFrame * > Interpreter : : pop_call_frame ( )
2023-07-01 13:56:40 +02:00
{
2023-07-20 10:46:42 +02:00
auto frame = m_call_frames . take_last ( ) ;
m_current_call_frame = m_call_frames . is_empty ( ) ? Span < Value > { } : this - > call_frame ( ) . registers ;
return frame ;
2023-07-01 13:56:40 +02:00
}
2021-06-03 10:46:30 +02:00
}
2023-09-27 10:10:00 +02:00
namespace JS : : Bytecode {
DeprecatedString Instruction : : to_deprecated_string ( Bytecode : : Executable const & executable ) const
{
# define __BYTECODE_OP(op) \
case Instruction : : Type : : op : \
return static_cast < Bytecode : : Op : : op const & > ( * this ) . to_deprecated_string_impl ( executable ) ;
switch ( type ( ) ) {
ENUMERATE_BYTECODE_OPS ( __BYTECODE_OP )
default :
VERIFY_NOT_REACHED ( ) ;
}
# undef __BYTECODE_OP
}
}
namespace JS : : Bytecode : : Op {
static ThrowCompletionOr < void > put_by_property_key ( VM & vm , Value base , Value this_value , Value value , PropertyKey name , PropertyKind kind )
{
auto object = TRY ( base . to_object ( vm ) ) ;
if ( kind = = PropertyKind : : Getter | | kind = = PropertyKind : : Setter ) {
// The generator should only pass us functions for getters and setters.
VERIFY ( value . is_function ( ) ) ;
}
switch ( kind ) {
case PropertyKind : : Getter : {
auto & function = value . as_function ( ) ;
if ( function . name ( ) . is_empty ( ) & & is < ECMAScriptFunctionObject > ( function ) )
static_cast < ECMAScriptFunctionObject * > ( & function ) - > set_name ( DeprecatedString : : formatted ( " get {} " , name ) ) ;
object - > define_direct_accessor ( name , & function , nullptr , Attribute : : Configurable | Attribute : : Enumerable ) ;
break ;
}
case PropertyKind : : Setter : {
auto & function = value . as_function ( ) ;
if ( function . name ( ) . is_empty ( ) & & is < ECMAScriptFunctionObject > ( function ) )
static_cast < ECMAScriptFunctionObject * > ( & function ) - > set_name ( DeprecatedString : : formatted ( " set {} " , name ) ) ;
object - > define_direct_accessor ( name , nullptr , & function , Attribute : : Configurable | Attribute : : Enumerable ) ;
break ;
}
case PropertyKind : : KeyValue : {
bool succeeded = TRY ( object - > internal_set ( name , value , this_value ) ) ;
if ( ! succeeded & & vm . in_strict_mode ( ) )
return vm . throw_completion < TypeError > ( ErrorType : : ReferenceNullishSetProperty , name , base . to_string_without_side_effects ( ) ) ;
break ;
}
case PropertyKind : : DirectKeyValue :
object - > define_direct_property ( name , value , Attribute : : Enumerable | Attribute : : Writable | Attribute : : Configurable ) ;
break ;
case PropertyKind : : Spread :
TRY ( object - > copy_data_properties ( vm , value , { } ) ) ;
break ;
case PropertyKind : : ProtoSetter :
if ( value . is_object ( ) | | value . is_null ( ) )
MUST ( object - > internal_set_prototype_of ( value . is_object ( ) ? & value . as_object ( ) : nullptr ) ) ;
break ;
}
return { } ;
}
2023-09-27 11:31:23 +02:00
ThrowCompletionOr < void > Load : : execute_impl ( Bytecode : : Interpreter & ) const
2023-09-27 10:10:00 +02:00
{
2023-09-27 11:31:23 +02:00
// Handled in the interpreter loop.
__builtin_unreachable ( ) ;
2023-09-27 10:10:00 +02:00
}
2023-09-27 11:31:23 +02:00
ThrowCompletionOr < void > LoadImmediate : : execute_impl ( Bytecode : : Interpreter & ) const
2023-09-27 10:10:00 +02:00
{
2023-09-27 11:31:23 +02:00
// Handled in the interpreter loop.
__builtin_unreachable ( ) ;
2023-09-27 10:10:00 +02:00
}
2023-09-27 11:31:23 +02:00
ThrowCompletionOr < void > Store : : execute_impl ( Bytecode : : Interpreter & ) const
2023-09-27 10:10:00 +02:00
{
2023-09-27 11:31:23 +02:00
// Handled in the interpreter loop.
__builtin_unreachable ( ) ;
2023-09-27 10:10:00 +02:00
}
static ThrowCompletionOr < Value > abstract_inequals ( VM & vm , Value src1 , Value src2 )
{
return Value ( ! TRY ( is_loosely_equal ( vm , src1 , src2 ) ) ) ;
}
static ThrowCompletionOr < Value > abstract_equals ( VM & vm , Value src1 , Value src2 )
{
return Value ( TRY ( is_loosely_equal ( vm , src1 , src2 ) ) ) ;
}
static ThrowCompletionOr < Value > typed_inequals ( VM & , Value src1 , Value src2 )
{
return Value ( ! is_strictly_equal ( src1 , src2 ) ) ;
}
static ThrowCompletionOr < Value > typed_equals ( VM & , Value src1 , Value src2 )
{
return Value ( is_strictly_equal ( src1 , src2 ) ) ;
}
# define JS_DEFINE_COMMON_BINARY_OP(OpTitleCase, op_snake_case) \
ThrowCompletionOr < void > OpTitleCase : : execute_impl ( Bytecode : : Interpreter & interpreter ) const \
{ \
auto & vm = interpreter . vm ( ) ; \
auto lhs = interpreter . reg ( m_lhs_reg ) ; \
auto rhs = interpreter . accumulator ( ) ; \
interpreter . accumulator ( ) = TRY ( op_snake_case ( vm , lhs , rhs ) ) ; \
return { } ; \
} \
DeprecatedString OpTitleCase : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const \
{ \
return DeprecatedString : : formatted ( # OpTitleCase " {} " , m_lhs_reg ) ; \
}
JS_ENUMERATE_COMMON_BINARY_OPS ( JS_DEFINE_COMMON_BINARY_OP )
static ThrowCompletionOr < Value > not_ ( VM & , Value value )
{
return Value ( ! value . to_boolean ( ) ) ;
}
static ThrowCompletionOr < Value > typeof_ ( VM & vm , Value value )
{
return PrimitiveString : : create ( vm , value . typeof ( ) ) ;
}
# define JS_DEFINE_COMMON_UNARY_OP(OpTitleCase, op_snake_case) \
ThrowCompletionOr < void > OpTitleCase : : execute_impl ( Bytecode : : Interpreter & interpreter ) const \
{ \
auto & vm = interpreter . vm ( ) ; \
interpreter . accumulator ( ) = TRY ( op_snake_case ( vm , interpreter . accumulator ( ) ) ) ; \
return { } ; \
} \
DeprecatedString OpTitleCase : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const \
{ \
return # OpTitleCase ; \
}
JS_ENUMERATE_COMMON_UNARY_OPS ( JS_DEFINE_COMMON_UNARY_OP )
ThrowCompletionOr < void > NewBigInt : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
interpreter . accumulator ( ) = BigInt : : create ( vm , m_bigint ) ;
return { } ;
}
ThrowCompletionOr < void > NewArray : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto array = MUST ( Array : : create ( interpreter . realm ( ) , 0 ) ) ;
for ( size_t i = 0 ; i < m_element_count ; i + + ) {
auto & value = interpreter . reg ( Register ( m_elements [ 0 ] . index ( ) + i ) ) ;
array - > indexed_properties ( ) . put ( i , value , default_attributes ) ;
}
interpreter . accumulator ( ) = array ;
return { } ;
}
ThrowCompletionOr < void > Append : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
// Note: This OpCode is used to construct array literals and argument arrays for calls,
// containing at least one spread element,
// Iterating over such a spread element to unpack it has to be visible by
// the user courtesy of
// (1) https://tc39.es/ecma262/#sec-runtime-semantics-arrayaccumulation
// SpreadElement : ... AssignmentExpression
// 1. Let spreadRef be ? Evaluation of AssignmentExpression.
// 2. Let spreadObj be ? GetValue(spreadRef).
// 3. Let iteratorRecord be ? GetIterator(spreadObj).
// 4. Repeat,
// a. Let next be ? IteratorStep(iteratorRecord).
// b. If next is false, return nextIndex.
// c. Let nextValue be ? IteratorValue(next).
// d. Perform ! CreateDataPropertyOrThrow(array, ! ToString(𝔽 (nextIndex)), nextValue).
// e. Set nextIndex to nextIndex + 1.
// (2) https://tc39.es/ecma262/#sec-runtime-semantics-argumentlistevaluation
// ArgumentList : ... AssignmentExpression
// 1. Let list be a new empty List.
// 2. Let spreadRef be ? Evaluation of AssignmentExpression.
// 3. Let spreadObj be ? GetValue(spreadRef).
// 4. Let iteratorRecord be ? GetIterator(spreadObj).
// 5. Repeat,
// a. Let next be ? IteratorStep(iteratorRecord).
// b. If next is false, return list.
// c. Let nextArg be ? IteratorValue(next).
// d. Append nextArg to list.
// ArgumentList : ArgumentList , ... AssignmentExpression
// 1. Let precedingArgs be ? ArgumentListEvaluation of ArgumentList.
// 2. Let spreadRef be ? Evaluation of AssignmentExpression.
// 3. Let iteratorRecord be ? GetIterator(? GetValue(spreadRef)).
// 4. Repeat,
// a. Let next be ? IteratorStep(iteratorRecord).
// b. If next is false, return precedingArgs.
// c. Let nextArg be ? IteratorValue(next).
// d. Append nextArg to precedingArgs.
auto & vm = interpreter . vm ( ) ;
// Note: We know from codegen, that lhs is a plain array with only indexed properties
auto & lhs = interpreter . reg ( m_lhs ) . as_array ( ) ;
auto lhs_size = lhs . indexed_properties ( ) . array_like_size ( ) ;
auto rhs = interpreter . accumulator ( ) ;
if ( m_is_spread ) {
// ...rhs
size_t i = lhs_size ;
TRY ( get_iterator_values ( vm , rhs , [ & i , & lhs ] ( Value iterator_value ) - > Optional < Completion > {
lhs . indexed_properties ( ) . put ( i , iterator_value , default_attributes ) ;
+ + i ;
return { } ;
} ) ) ;
} else {
lhs . indexed_properties ( ) . put ( lhs_size , rhs , default_attributes ) ;
}
return { } ;
}
ThrowCompletionOr < void > ImportCall : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto specifier = interpreter . reg ( m_specifier ) ;
auto options_value = interpreter . reg ( m_options ) ;
interpreter . accumulator ( ) = TRY ( perform_import_call ( vm , specifier , options_value ) ) ;
return { } ;
}
// FIXME: Since the accumulator is a Value, we store an object there and have to convert back and forth between that an Iterator records. Not great.
// Make sure to put this into the accumulator before the iterator object disappears from the stack to prevent the members from being GC'd.
static Object * iterator_to_object ( VM & vm , IteratorRecord iterator )
{
auto & realm = * vm . current_realm ( ) ;
auto object = Object : : create ( realm , nullptr ) ;
object - > define_direct_property ( vm . names . iterator , iterator . iterator , 0 ) ;
object - > define_direct_property ( vm . names . next , iterator . next_method , 0 ) ;
object - > define_direct_property ( vm . names . done , Value ( iterator . done ) , 0 ) ;
return object ;
}
static IteratorRecord object_to_iterator ( VM & vm , Object & object )
{
return IteratorRecord {
. iterator = & MUST ( object . get ( vm . names . iterator ) ) . as_object ( ) ,
. next_method = MUST ( object . get ( vm . names . next ) ) ,
. done = MUST ( object . get ( vm . names . done ) ) . as_bool ( )
} ;
}
ThrowCompletionOr < void > IteratorToArray : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto iterator_object = TRY ( interpreter . accumulator ( ) . to_object ( vm ) ) ;
auto iterator = object_to_iterator ( vm , iterator_object ) ;
auto array = MUST ( Array : : create ( interpreter . realm ( ) , 0 ) ) ;
size_t index = 0 ;
while ( true ) {
auto iterator_result = TRY ( iterator_next ( vm , iterator ) ) ;
auto complete = TRY ( iterator_complete ( vm , iterator_result ) ) ;
if ( complete ) {
interpreter . accumulator ( ) = array ;
return { } ;
}
auto value = TRY ( iterator_value ( vm , iterator_result ) ) ;
MUST ( array - > create_data_property_or_throw ( index , value ) ) ;
index + + ;
}
return { } ;
}
ThrowCompletionOr < void > NewString : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
interpreter . accumulator ( ) = PrimitiveString : : create ( interpreter . vm ( ) , interpreter . current_executable ( ) . get_string ( m_string ) ) ;
return { } ;
}
ThrowCompletionOr < void > NewObject : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto & realm = * vm . current_realm ( ) ;
interpreter . accumulator ( ) = Object : : create ( realm , realm . intrinsics ( ) . object_prototype ( ) ) ;
return { } ;
}
// 13.2.7.3 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-regular-expression-literals-runtime-semantics-evaluation
ThrowCompletionOr < void > NewRegExp : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto & realm = * vm . current_realm ( ) ;
// 1. Let pattern be CodePointsToString(BodyText of RegularExpressionLiteral).
auto pattern = interpreter . current_executable ( ) . get_string ( m_source_index ) ;
// 2. Let flags be CodePointsToString(FlagText of RegularExpressionLiteral).
auto flags = interpreter . current_executable ( ) . get_string ( m_flags_index ) ;
// 3. Return ! RegExpCreate(pattern, flags).
auto & parsed_regex = interpreter . current_executable ( ) . regex_table - > get ( m_regex_index ) ;
Regex < ECMA262 > regex ( parsed_regex . regex , parsed_regex . pattern , parsed_regex . flags ) ;
// NOTE: We bypass RegExpCreate and subsequently RegExpAlloc as an optimization to use the already parsed values.
auto regexp_object = RegExpObject : : create ( realm , move ( regex ) , move ( pattern ) , move ( flags ) ) ;
// RegExpAlloc has these two steps from the 'Legacy RegExp features' proposal.
regexp_object - > set_realm ( * vm . current_realm ( ) ) ;
// We don't need to check 'If SameValue(newTarget, thisRealm.[[Intrinsics]].[[%RegExp%]]) is true'
// here as we know RegExpCreate calls RegExpAlloc with %RegExp% for newTarget.
regexp_object - > set_legacy_features_enabled ( true ) ;
interpreter . accumulator ( ) = regexp_object ;
return { } ;
}
# define JS_DEFINE_NEW_BUILTIN_ERROR_OP(ErrorName) \
ThrowCompletionOr < void > New # # ErrorName : : execute_impl ( Bytecode : : Interpreter & interpreter ) const \
{ \
auto & vm = interpreter . vm ( ) ; \
auto & realm = * vm . current_realm ( ) ; \
interpreter . accumulator ( ) = ErrorName : : create ( realm , interpreter . current_executable ( ) . get_string ( m_error_string ) ) ; \
return { } ; \
} \
DeprecatedString New # # ErrorName : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const \
{ \
return DeprecatedString : : formatted ( " New " # ErrorName " {} ( \" {} \" ) " , m_error_string , executable . string_table - > get ( m_error_string ) ) ; \
}
JS_ENUMERATE_NEW_BUILTIN_ERROR_OPS ( JS_DEFINE_NEW_BUILTIN_ERROR_OP )
ThrowCompletionOr < void > CopyObjectExcludingProperties : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto & realm = * vm . current_realm ( ) ;
auto from_object = interpreter . reg ( m_from_object ) ;
auto to_object = Object : : create ( realm , realm . intrinsics ( ) . object_prototype ( ) ) ;
HashTable < PropertyKey > excluded_names ;
for ( size_t i = 0 ; i < m_excluded_names_count ; + + i ) {
excluded_names . set ( TRY ( interpreter . reg ( m_excluded_names [ i ] ) . to_property_key ( vm ) ) ) ;
}
TRY ( to_object - > copy_data_properties ( vm , from_object , excluded_names ) ) ;
interpreter . accumulator ( ) = to_object ;
return { } ;
}
ThrowCompletionOr < void > ConcatString : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto string = TRY ( interpreter . accumulator ( ) . to_primitive_string ( vm ) ) ;
interpreter . reg ( m_lhs ) = PrimitiveString : : create ( vm , interpreter . reg ( m_lhs ) . as_string ( ) , string ) ;
return { } ;
}
ThrowCompletionOr < void > GetVariable : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
2023-10-26 10:39:40 +02:00
auto & cached_environment_coordinate = interpreter . current_executable ( ) . environment_variable_caches [ m_cache_index ] ;
if ( cached_environment_coordinate . has_value ( ) ) {
2023-10-07 18:15:49 +02:00
auto environment = vm . running_execution_context ( ) . lexical_environment ;
2023-10-26 10:39:40 +02:00
for ( size_t i = 0 ; i < cached_environment_coordinate - > hops ; + + i )
2023-10-07 18:15:49 +02:00
environment = environment - > outer_environment ( ) ;
VERIFY ( environment ) ;
VERIFY ( environment - > is_declarative_environment ( ) ) ;
if ( ! environment - > is_permanently_screwed_by_eval ( ) ) {
2023-10-26 10:39:40 +02:00
interpreter . accumulator ( ) = TRY ( verify_cast < DeclarativeEnvironment > ( * environment ) . get_binding_value_direct ( vm , cached_environment_coordinate . value ( ) . index , vm . in_strict_mode ( ) ) ) ;
2023-10-07 18:15:49 +02:00
return { } ;
2023-09-27 10:10:00 +02:00
}
2023-10-26 10:39:40 +02:00
cached_environment_coordinate = { } ;
2023-10-07 18:15:49 +02:00
}
2023-09-27 10:10:00 +02:00
2023-10-07 18:15:49 +02:00
auto const & string = interpreter . current_executable ( ) . get_identifier ( m_identifier ) ;
auto reference = TRY ( vm . resolve_binding ( string ) ) ;
if ( reference . environment_coordinate ( ) . has_value ( ) )
2023-10-26 10:39:40 +02:00
cached_environment_coordinate = reference . environment_coordinate ( ) ;
2023-09-27 10:10:00 +02:00
interpreter . accumulator ( ) = TRY ( reference . get_value ( vm ) ) ;
return { } ;
}
ThrowCompletionOr < void > GetCalleeAndThisFromEnvironment : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
2023-10-26 10:39:40 +02:00
auto & cached_environment_coordinate = interpreter . current_executable ( ) . environment_variable_caches [ m_cache_index ] ;
if ( cached_environment_coordinate . has_value ( ) ) {
2023-10-07 18:15:49 +02:00
auto environment = vm . running_execution_context ( ) . lexical_environment ;
2023-10-26 10:39:40 +02:00
for ( size_t i = 0 ; i < cached_environment_coordinate - > hops ; + + i )
2023-10-07 18:15:49 +02:00
environment = environment - > outer_environment ( ) ;
VERIFY ( environment ) ;
VERIFY ( environment - > is_declarative_environment ( ) ) ;
if ( ! environment - > is_permanently_screwed_by_eval ( ) ) {
2023-10-26 10:39:40 +02:00
interpreter . reg ( m_callee_reg ) = TRY ( verify_cast < DeclarativeEnvironment > ( * environment ) . get_binding_value_direct ( vm , cached_environment_coordinate . value ( ) . index , vm . in_strict_mode ( ) ) ) ;
2023-10-07 18:15:49 +02:00
Value this_value = js_undefined ( ) ;
if ( auto base_object = environment - > with_base_object ( ) )
this_value = base_object ;
interpreter . reg ( m_this_reg ) = this_value ;
return { } ;
2023-09-27 10:10:00 +02:00
}
2023-10-26 10:39:40 +02:00
cached_environment_coordinate = { } ;
2023-10-07 18:15:49 +02:00
}
auto const & string = interpreter . current_executable ( ) . get_identifier ( m_identifier ) ;
auto reference = TRY ( vm . resolve_binding ( string ) ) ;
if ( reference . environment_coordinate ( ) . has_value ( ) )
2023-10-26 10:39:40 +02:00
cached_environment_coordinate = reference . environment_coordinate ( ) ;
2023-09-27 10:10:00 +02:00
interpreter . reg ( m_callee_reg ) = TRY ( reference . get_value ( vm ) ) ;
Value this_value = js_undefined ( ) ;
if ( reference . is_property_reference ( ) ) {
this_value = reference . get_this_value ( ) ;
} else {
if ( reference . is_environment_reference ( ) ) {
if ( auto base_object = reference . base_environment ( ) . with_base_object ( ) ; base_object ! = nullptr )
this_value = base_object ;
}
}
interpreter . reg ( m_this_reg ) = this_value ;
return { } ;
}
ThrowCompletionOr < void > GetGlobal : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto & realm = * vm . current_realm ( ) ;
auto & cache = interpreter . current_executable ( ) . global_variable_caches [ m_cache_index ] ;
auto & binding_object = realm . global_environment ( ) . object_record ( ) . binding_object ( ) ;
auto & declarative_record = realm . global_environment ( ) . declarative_record ( ) ;
// OPTIMIZATION: If the shape of the object hasn't changed, we can use the cached property offset.
// NOTE: Unique shapes don't change identity, so we compare their serial numbers instead.
auto & shape = binding_object . shape ( ) ;
if ( cache . environment_serial_number = = declarative_record . environment_serial_number ( )
& & & shape = = cache . shape
& & ( ! shape . is_unique ( ) | | shape . unique_shape_serial_number ( ) = = cache . unique_shape_serial_number ) ) {
interpreter . accumulator ( ) = binding_object . get_direct ( cache . property_offset . value ( ) ) ;
return { } ;
}
cache . environment_serial_number = declarative_record . environment_serial_number ( ) ;
2023-10-08 11:18:33 +02:00
auto const & name = interpreter . current_executable ( ) . get_identifier ( m_identifier ) ;
2023-09-27 10:10:00 +02:00
if ( vm . running_execution_context ( ) . script_or_module . has < NonnullGCPtr < Module > > ( ) ) {
// NOTE: GetGlobal is used to access variables stored in the module environment and global environment.
// The module environment is checked first since it precedes the global environment in the environment chain.
auto & module_environment = * vm . running_execution_context ( ) . script_or_module . get < NonnullGCPtr < Module > > ( ) - > environment ( ) ;
if ( TRY ( module_environment . has_binding ( name ) ) ) {
// TODO: Cache offset of binding value
interpreter . accumulator ( ) = TRY ( module_environment . get_binding_value ( vm , name , vm . in_strict_mode ( ) ) ) ;
return { } ;
}
}
if ( TRY ( declarative_record . has_binding ( name ) ) ) {
// TODO: Cache offset of binding value
interpreter . accumulator ( ) = TRY ( declarative_record . get_binding_value ( vm , name , vm . in_strict_mode ( ) ) ) ;
return { } ;
}
if ( TRY ( binding_object . has_property ( name ) ) ) {
CacheablePropertyMetadata cacheable_metadata ;
interpreter . accumulator ( ) = js_undefined ( ) ;
interpreter . accumulator ( ) = TRY ( binding_object . internal_get ( name , interpreter . accumulator ( ) , & cacheable_metadata ) ) ;
if ( cacheable_metadata . type = = CacheablePropertyMetadata : : Type : : OwnProperty ) {
cache . shape = shape ;
cache . property_offset = cacheable_metadata . property_offset . value ( ) ;
cache . unique_shape_serial_number = shape . unique_shape_serial_number ( ) ;
}
return { } ;
}
return vm . throw_completion < ReferenceError > ( ErrorType : : UnknownIdentifier , name ) ;
}
2023-09-27 11:31:23 +02:00
ThrowCompletionOr < void > GetLocal : : execute_impl ( Bytecode : : Interpreter & ) const
2023-09-27 10:10:00 +02:00
{
2023-09-27 11:31:23 +02:00
// Handled in the interpreter loop.
__builtin_unreachable ( ) ;
2023-09-27 10:10:00 +02:00
}
ThrowCompletionOr < void > DeleteVariable : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto const & string = interpreter . current_executable ( ) . get_identifier ( m_identifier ) ;
auto reference = TRY ( vm . resolve_binding ( string ) ) ;
interpreter . accumulator ( ) = Value ( TRY ( reference . delete_ ( vm ) ) ) ;
return { } ;
}
ThrowCompletionOr < void > CreateLexicalEnvironment : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto make_and_swap_envs = [ & ] ( auto & old_environment ) {
GCPtr < Environment > environment = new_declarative_environment ( * old_environment ) . ptr ( ) ;
swap ( old_environment , environment ) ;
return environment ;
} ;
interpreter . saved_lexical_environment_stack ( ) . append ( make_and_swap_envs ( interpreter . vm ( ) . running_execution_context ( ) . lexical_environment ) ) ;
return { } ;
}
ThrowCompletionOr < void > EnterObjectEnvironment : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto & old_environment = vm . running_execution_context ( ) . lexical_environment ;
interpreter . saved_lexical_environment_stack ( ) . append ( old_environment ) ;
auto object = TRY ( interpreter . accumulator ( ) . to_object ( vm ) ) ;
vm . running_execution_context ( ) . lexical_environment = new_object_environment ( object , true , old_environment ) ;
return { } ;
}
ThrowCompletionOr < void > CreateVariable : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto const & name = interpreter . current_executable ( ) . get_identifier ( m_identifier ) ;
if ( m_mode = = EnvironmentMode : : Lexical ) {
VERIFY ( ! m_is_global ) ;
// Note: This is papering over an issue where "FunctionDeclarationInstantiation" creates these bindings for us.
// Instead of crashing in there, we'll just raise an exception here.
if ( TRY ( vm . lexical_environment ( ) - > has_binding ( name ) ) )
return vm . throw_completion < InternalError > ( TRY_OR_THROW_OOM ( vm , String : : formatted ( " Lexical environment already has binding '{}' " , name ) ) ) ;
if ( m_is_immutable )
return vm . lexical_environment ( ) - > create_immutable_binding ( vm , name , m_is_strict ) ;
else
return vm . lexical_environment ( ) - > create_mutable_binding ( vm , name , m_is_strict ) ;
} else {
if ( ! m_is_global ) {
if ( m_is_immutable )
return vm . variable_environment ( ) - > create_immutable_binding ( vm , name , m_is_strict ) ;
else
return vm . variable_environment ( ) - > create_mutable_binding ( vm , name , m_is_strict ) ;
} else {
// NOTE: CreateVariable with m_is_global set to true is expected to only be used in GlobalDeclarationInstantiation currently, which only uses "false" for "can_be_deleted".
// The only area that sets "can_be_deleted" to true is EvalDeclarationInstantiation, which is currently fully implemented in C++ and not in Bytecode.
return verify_cast < GlobalEnvironment > ( vm . variable_environment ( ) ) - > create_global_var_binding ( name , false ) ;
}
}
return { } ;
}
ThrowCompletionOr < void > SetVariable : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto const & name = interpreter . current_executable ( ) . get_identifier ( m_identifier ) ;
auto environment = m_mode = = EnvironmentMode : : Lexical ? vm . running_execution_context ( ) . lexical_environment : vm . running_execution_context ( ) . variable_environment ;
auto reference = TRY ( vm . resolve_binding ( name , environment ) ) ;
switch ( m_initialization_mode ) {
case InitializationMode : : Initialize :
TRY ( reference . initialize_referenced_binding ( vm , interpreter . accumulator ( ) ) ) ;
break ;
case InitializationMode : : Set :
TRY ( reference . put_value ( vm , interpreter . accumulator ( ) ) ) ;
break ;
}
return { } ;
}
2023-09-27 11:31:23 +02:00
ThrowCompletionOr < void > SetLocal : : execute_impl ( Bytecode : : Interpreter & ) const
2023-09-27 10:10:00 +02:00
{
2023-09-27 11:31:23 +02:00
// Handled in the interpreter loop.
__builtin_unreachable ( ) ;
2023-09-27 10:10:00 +02:00
}
static ThrowCompletionOr < NonnullGCPtr < Object > > base_object_for_get ( Bytecode : : Interpreter & interpreter , Value base_value )
{
auto & vm = interpreter . vm ( ) ;
if ( base_value . is_object ( ) )
return base_value . as_object ( ) ;
// OPTIMIZATION: For various primitives we can avoid actually creating a new object for them.
if ( base_value . is_string ( ) )
return vm . current_realm ( ) - > intrinsics ( ) . string_prototype ( ) ;
if ( base_value . is_number ( ) )
return vm . current_realm ( ) - > intrinsics ( ) . number_prototype ( ) ;
if ( base_value . is_boolean ( ) )
return vm . current_realm ( ) - > intrinsics ( ) . boolean_prototype ( ) ;
return base_value . to_object ( vm ) ;
}
static ThrowCompletionOr < void > get_by_id ( Bytecode : : Interpreter & interpreter , IdentifierTableIndex property , Value base_value , Value this_value , u32 cache_index )
{
auto & vm = interpreter . vm ( ) ;
auto const & name = interpreter . current_executable ( ) . get_identifier ( property ) ;
auto & cache = interpreter . current_executable ( ) . property_lookup_caches [ cache_index ] ;
if ( base_value . is_string ( ) ) {
auto string_value = TRY ( base_value . as_string ( ) . get ( vm , name ) ) ;
if ( string_value . has_value ( ) ) {
interpreter . accumulator ( ) = * string_value ;
return { } ;
}
}
auto base_obj = TRY ( base_object_for_get ( interpreter , base_value ) ) ;
// OPTIMIZATION: If the shape of the object hasn't changed, we can use the cached property offset.
// NOTE: Unique shapes don't change identity, so we compare their serial numbers instead.
auto & shape = base_obj - > shape ( ) ;
if ( & shape = = cache . shape
& & ( ! shape . is_unique ( ) | | shape . unique_shape_serial_number ( ) = = cache . unique_shape_serial_number ) ) {
interpreter . accumulator ( ) = base_obj - > get_direct ( cache . property_offset . value ( ) ) ;
return { } ;
}
CacheablePropertyMetadata cacheable_metadata ;
interpreter . accumulator ( ) = TRY ( base_obj - > internal_get ( name , this_value , & cacheable_metadata ) ) ;
if ( cacheable_metadata . type = = CacheablePropertyMetadata : : Type : : OwnProperty ) {
cache . shape = shape ;
cache . property_offset = cacheable_metadata . property_offset . value ( ) ;
cache . unique_shape_serial_number = shape . unique_shape_serial_number ( ) ;
}
return { } ;
}
ThrowCompletionOr < void > GetById : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto base_value = interpreter . accumulator ( ) ;
return get_by_id ( interpreter , m_property , base_value , base_value , m_cache_index ) ;
}
ThrowCompletionOr < void > GetByIdWithThis : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto base_value = interpreter . accumulator ( ) ;
auto this_value = interpreter . reg ( m_this_value ) ;
return get_by_id ( interpreter , m_property , base_value , this_value , m_cache_index ) ;
}
ThrowCompletionOr < void > GetPrivateById : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto const & name = interpreter . current_executable ( ) . get_identifier ( m_property ) ;
auto base_value = interpreter . accumulator ( ) ;
auto private_reference = make_private_reference ( vm , base_value , name ) ;
interpreter . accumulator ( ) = TRY ( private_reference . get_value ( vm ) ) ;
return { } ;
}
ThrowCompletionOr < void > HasPrivateId : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
if ( ! interpreter . accumulator ( ) . is_object ( ) )
return vm . throw_completion < TypeError > ( ErrorType : : InOperatorWithObject ) ;
auto private_environment = vm . running_execution_context ( ) . private_environment ;
VERIFY ( private_environment ) ;
auto private_name = private_environment - > resolve_private_identifier ( interpreter . current_executable ( ) . get_identifier ( m_property ) ) ;
interpreter . accumulator ( ) = Value ( interpreter . accumulator ( ) . as_object ( ) . private_element_find ( private_name ) ! = nullptr ) ;
return { } ;
}
ThrowCompletionOr < void > PutById : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
// NOTE: Get the value from the accumulator before side effects have a chance to overwrite it.
auto value = interpreter . accumulator ( ) ;
auto base = interpreter . reg ( m_base ) ;
PropertyKey name = interpreter . current_executable ( ) . get_identifier ( m_property ) ;
TRY ( put_by_property_key ( vm , base , base , value , name , m_kind ) ) ;
interpreter . accumulator ( ) = value ;
return { } ;
}
ThrowCompletionOr < void > PutByIdWithThis : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
// NOTE: Get the value from the accumulator before side effects have a chance to overwrite it.
auto value = interpreter . accumulator ( ) ;
auto base = interpreter . reg ( m_base ) ;
PropertyKey name = interpreter . current_executable ( ) . get_identifier ( m_property ) ;
TRY ( put_by_property_key ( vm , base , interpreter . reg ( m_this_value ) , value , name , m_kind ) ) ;
interpreter . accumulator ( ) = value ;
return { } ;
}
ThrowCompletionOr < void > PutPrivateById : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
// NOTE: Get the value from the accumulator before side effects have a chance to overwrite it.
auto value = interpreter . accumulator ( ) ;
auto object = TRY ( interpreter . reg ( m_base ) . to_object ( vm ) ) ;
auto name = interpreter . current_executable ( ) . get_identifier ( m_property ) ;
auto private_reference = make_private_reference ( vm , object , name ) ;
TRY ( private_reference . put_value ( vm , value ) ) ;
interpreter . accumulator ( ) = value ;
return { } ;
}
ThrowCompletionOr < void > DeleteById : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto base_value = interpreter . accumulator ( ) ;
auto const & identifier = interpreter . current_executable ( ) . get_identifier ( m_property ) ;
bool strict = vm . in_strict_mode ( ) ;
auto reference = Reference { base_value , identifier , { } , strict } ;
interpreter . accumulator ( ) = Value ( TRY ( reference . delete_ ( vm ) ) ) ;
return { } ;
}
ThrowCompletionOr < void > DeleteByIdWithThis : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto base_value = interpreter . accumulator ( ) ;
auto const & identifier = interpreter . current_executable ( ) . get_identifier ( m_property ) ;
bool strict = vm . in_strict_mode ( ) ;
auto reference = Reference { base_value , identifier , interpreter . reg ( m_this_value ) , strict } ;
interpreter . accumulator ( ) = Value ( TRY ( reference . delete_ ( vm ) ) ) ;
return { } ;
}
ThrowCompletionOr < void > Jump : : execute_impl ( Bytecode : : Interpreter & ) const
{
// Handled in the interpreter loop.
2023-09-27 11:31:23 +02:00
__builtin_unreachable ( ) ;
2023-09-27 10:10:00 +02:00
}
ThrowCompletionOr < void > ResolveThisBinding : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & cached_this_value = interpreter . reg ( Register : : this_value ( ) ) ;
if ( cached_this_value . is_empty ( ) ) {
// OPTIMIZATION: Because the value of 'this' cannot be reassigned during a function execution, it's
// resolved once and then saved for subsequent use.
auto & vm = interpreter . vm ( ) ;
cached_this_value = TRY ( vm . resolve_this_binding ( ) ) ;
}
interpreter . accumulator ( ) = cached_this_value ;
return { } ;
}
// https://tc39.es/ecma262/#sec-makesuperpropertyreference
ThrowCompletionOr < void > ResolveSuperBase : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
// 1. Let env be GetThisEnvironment().
auto & env = verify_cast < FunctionEnvironment > ( * get_this_environment ( vm ) ) ;
// 2. Assert: env.HasSuperBinding() is true.
VERIFY ( env . has_super_binding ( ) ) ;
// 3. Let baseValue be ? env.GetSuperBase().
interpreter . accumulator ( ) = TRY ( env . get_super_base ( ) ) ;
return { } ;
}
ThrowCompletionOr < void > GetNewTarget : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
interpreter . accumulator ( ) = interpreter . vm ( ) . get_new_target ( ) ;
return { } ;
}
ThrowCompletionOr < void > GetImportMeta : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
interpreter . accumulator ( ) = interpreter . vm ( ) . get_import_meta ( ) ;
return { } ;
}
ThrowCompletionOr < void > JumpConditional : : execute_impl ( Bytecode : : Interpreter & ) const
{
// Handled in the interpreter loop.
2023-09-27 11:31:23 +02:00
__builtin_unreachable ( ) ;
2023-09-27 10:10:00 +02:00
}
ThrowCompletionOr < void > JumpNullish : : execute_impl ( Bytecode : : Interpreter & ) const
{
// Handled in the interpreter loop.
2023-09-27 11:31:23 +02:00
__builtin_unreachable ( ) ;
2023-09-27 10:10:00 +02:00
}
ThrowCompletionOr < void > JumpUndefined : : execute_impl ( Bytecode : : Interpreter & ) const
{
// Handled in the interpreter loop.
2023-09-27 11:31:23 +02:00
__builtin_unreachable ( ) ;
2023-09-27 10:10:00 +02:00
}
// 13.3.8.1 https://tc39.es/ecma262/#sec-runtime-semantics-argumentlistevaluation
static MarkedVector < Value > argument_list_evaluation ( Bytecode : : Interpreter & interpreter )
{
// Note: Any spreading and actual evaluation is handled in preceding opcodes
// Note: The spec uses the concept of a list, while we create a temporary array
// in the preceding opcodes, so we have to convert in a manner that is not
// visible to the user
auto & vm = interpreter . vm ( ) ;
MarkedVector < Value > argument_values { vm . heap ( ) } ;
auto arguments = interpreter . accumulator ( ) ;
auto & argument_array = arguments . as_array ( ) ;
auto array_length = argument_array . indexed_properties ( ) . array_like_size ( ) ;
argument_values . ensure_capacity ( array_length ) ;
for ( size_t i = 0 ; i < array_length ; + + i ) {
if ( auto maybe_value = argument_array . indexed_properties ( ) . get ( i ) ; maybe_value . has_value ( ) )
argument_values . append ( maybe_value . release_value ( ) . value ) ;
else
argument_values . append ( js_undefined ( ) ) ;
}
return argument_values ;
}
static Completion throw_type_error_for_callee ( Bytecode : : Interpreter & interpreter , auto & call , StringView callee_type )
{
auto & vm = interpreter . vm ( ) ;
auto callee = interpreter . reg ( call . callee ( ) ) ;
if ( call . expression_string ( ) . has_value ( ) )
return vm . throw_completion < TypeError > ( ErrorType : : IsNotAEvaluatedFrom , callee . to_string_without_side_effects ( ) , callee_type , interpreter . current_executable ( ) . get_string ( call . expression_string ( ) - > value ( ) ) ) ;
return vm . throw_completion < TypeError > ( ErrorType : : IsNotA , callee . to_string_without_side_effects ( ) , callee_type ) ;
}
static ThrowCompletionOr < void > throw_if_needed_for_call ( Interpreter & interpreter , auto & call , Value callee )
{
if ( call . call_type ( ) = = CallType : : Call & & ! callee . is_function ( ) )
return throw_type_error_for_callee ( interpreter , call , " function " sv ) ;
if ( call . call_type ( ) = = CallType : : Construct & & ! callee . is_constructor ( ) )
return throw_type_error_for_callee ( interpreter , call , " constructor " sv ) ;
return { } ;
}
static ThrowCompletionOr < void > perform_call ( Interpreter & interpreter , auto & call , Value callee , MarkedVector < Value > argument_values )
{
auto & vm = interpreter . vm ( ) ;
auto this_value = interpreter . reg ( call . this_value ( ) ) ;
auto & function = callee . as_function ( ) ;
Value return_value ;
if ( call . call_type ( ) = = CallType : : DirectEval ) {
if ( callee = = interpreter . realm ( ) . intrinsics ( ) . eval_function ( ) )
return_value = TRY ( perform_eval ( vm , ! argument_values . is_empty ( ) ? argument_values [ 0 ] . value_or ( JS : : js_undefined ( ) ) : js_undefined ( ) , vm . in_strict_mode ( ) ? CallerMode : : Strict : CallerMode : : NonStrict , EvalMode : : Direct ) ) ;
else
return_value = TRY ( JS : : call ( vm , function , this_value , move ( argument_values ) ) ) ;
} else if ( call . call_type ( ) = = CallType : : Call )
return_value = TRY ( JS : : call ( vm , function , this_value , move ( argument_values ) ) ) ;
else
return_value = TRY ( construct ( vm , function , move ( argument_values ) ) ) ;
interpreter . accumulator ( ) = return_value ;
return { } ;
}
ThrowCompletionOr < void > Call : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto callee = interpreter . reg ( m_callee ) ;
TRY ( throw_if_needed_for_call ( interpreter , * this , callee ) ) ;
MarkedVector < Value > argument_values ( vm . heap ( ) ) ;
argument_values . ensure_capacity ( m_argument_count ) ;
for ( u32 i = 0 ; i < m_argument_count ; + + i ) {
argument_values . unchecked_append ( interpreter . reg ( Register { m_first_argument . index ( ) + i } ) ) ;
}
return perform_call ( interpreter , * this , callee , move ( argument_values ) ) ;
}
ThrowCompletionOr < void > CallWithArgumentArray : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto callee = interpreter . reg ( m_callee ) ;
TRY ( throw_if_needed_for_call ( interpreter , * this , callee ) ) ;
auto argument_values = argument_list_evaluation ( interpreter ) ;
return perform_call ( interpreter , * this , callee , move ( argument_values ) ) ;
}
// 13.3.7.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
ThrowCompletionOr < void > SuperCallWithArgumentArray : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
// 1. Let newTarget be GetNewTarget().
auto new_target = vm . get_new_target ( ) ;
// 2. Assert: Type(newTarget) is Object.
VERIFY ( new_target . is_object ( ) ) ;
// 3. Let func be GetSuperConstructor().
auto * func = get_super_constructor ( vm ) ;
// 4. Let argList be ? ArgumentListEvaluation of Arguments.
MarkedVector < Value > arg_list { vm . heap ( ) } ;
if ( m_is_synthetic ) {
auto const & value = interpreter . accumulator ( ) ;
VERIFY ( value . is_object ( ) & & is < Array > ( value . as_object ( ) ) ) ;
auto const & array_value = static_cast < Array const & > ( value . as_object ( ) ) ;
auto length = MUST ( length_of_array_like ( vm , array_value ) ) ;
for ( size_t i = 0 ; i < length ; + + i )
arg_list . append ( array_value . get_without_side_effects ( PropertyKey { i } ) ) ;
} else {
arg_list = argument_list_evaluation ( interpreter ) ;
}
// 5. If IsConstructor(func) is false, throw a TypeError exception.
if ( ! Value ( func ) . is_constructor ( ) )
return vm . throw_completion < TypeError > ( ErrorType : : NotAConstructor , " Super constructor " ) ;
// 6. Let result be ? Construct(func, argList, newTarget).
auto result = TRY ( construct ( vm , static_cast < FunctionObject & > ( * func ) , move ( arg_list ) , & new_target . as_function ( ) ) ) ;
// 7. Let thisER be GetThisEnvironment().
auto & this_environment = verify_cast < FunctionEnvironment > ( * get_this_environment ( vm ) ) ;
// 8. Perform ? thisER.BindThisValue(result).
TRY ( this_environment . bind_this_value ( vm , result ) ) ;
// 9. Let F be thisER.[[FunctionObject]].
auto & f = this_environment . function_object ( ) ;
// 10. Assert: F is an ECMAScript function object.
// NOTE: This is implied by the strong C++ type.
// 11. Perform ? InitializeInstanceElements(result, F).
TRY ( result - > initialize_instance_elements ( f ) ) ;
// 12. Return result.
interpreter . accumulator ( ) = result ;
return { } ;
}
ThrowCompletionOr < void > NewFunction : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
if ( ! m_function_node . has_name ( ) ) {
DeprecatedFlyString name = { } ;
if ( m_lhs_name . has_value ( ) )
name = interpreter . current_executable ( ) . get_identifier ( m_lhs_name . value ( ) ) ;
interpreter . accumulator ( ) = m_function_node . instantiate_ordinary_function_expression ( vm , name ) ;
} else {
interpreter . accumulator ( ) = ECMAScriptFunctionObject : : create ( interpreter . realm ( ) , m_function_node . name ( ) , m_function_node . source_text ( ) , m_function_node . body ( ) , m_function_node . parameters ( ) , m_function_node . function_length ( ) , m_function_node . local_variables_names ( ) , vm . lexical_environment ( ) , vm . running_execution_context ( ) . private_environment , m_function_node . kind ( ) , m_function_node . is_strict_mode ( ) , m_function_node . might_need_arguments_object ( ) , m_function_node . contains_direct_call_to_eval ( ) , m_function_node . is_arrow_function ( ) ) ;
}
if ( m_home_object . has_value ( ) ) {
auto home_object_value = interpreter . reg ( m_home_object . value ( ) ) ;
static_cast < ECMAScriptFunctionObject & > ( interpreter . accumulator ( ) . as_function ( ) ) . set_home_object ( & home_object_value . as_object ( ) ) ;
}
return { } ;
}
ThrowCompletionOr < void > Return : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
interpreter . do_return ( interpreter . accumulator ( ) . value_or ( js_undefined ( ) ) ) ;
return { } ;
}
ThrowCompletionOr < void > Increment : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto old_value = TRY ( interpreter . accumulator ( ) . to_numeric ( vm ) ) ;
if ( old_value . is_number ( ) )
interpreter . accumulator ( ) = Value ( old_value . as_double ( ) + 1 ) ;
else
interpreter . accumulator ( ) = BigInt : : create ( vm , old_value . as_bigint ( ) . big_integer ( ) . plus ( Crypto : : SignedBigInteger { 1 } ) ) ;
return { } ;
}
ThrowCompletionOr < void > Decrement : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto old_value = TRY ( interpreter . accumulator ( ) . to_numeric ( vm ) ) ;
if ( old_value . is_number ( ) )
interpreter . accumulator ( ) = Value ( old_value . as_double ( ) - 1 ) ;
else
interpreter . accumulator ( ) = BigInt : : create ( vm , old_value . as_bigint ( ) . big_integer ( ) . minus ( Crypto : : SignedBigInteger { 1 } ) ) ;
return { } ;
}
ThrowCompletionOr < void > Throw : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
return throw_completion ( interpreter . accumulator ( ) ) ;
}
ThrowCompletionOr < void > ThrowIfNotObject : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
if ( ! interpreter . accumulator ( ) . is_object ( ) )
return vm . throw_completion < TypeError > ( ErrorType : : NotAnObject , interpreter . accumulator ( ) . to_string_without_side_effects ( ) ) ;
return { } ;
}
ThrowCompletionOr < void > ThrowIfNullish : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto value = interpreter . accumulator ( ) ;
if ( value . is_nullish ( ) )
return vm . throw_completion < TypeError > ( ErrorType : : NotObjectCoercible , value . to_string_without_side_effects ( ) ) ;
return { } ;
}
ThrowCompletionOr < void > EnterUnwindContext : : execute_impl ( Bytecode : : Interpreter & ) const
{
// Handled in the interpreter loop.
2023-09-27 11:31:23 +02:00
__builtin_unreachable ( ) ;
2023-09-27 10:10:00 +02:00
}
ThrowCompletionOr < void > ScheduleJump : : execute_impl ( Bytecode : : Interpreter & ) const
{
// Handled in the interpreter loop.
2023-09-27 11:31:23 +02:00
__builtin_unreachable ( ) ;
2023-09-27 10:10:00 +02:00
}
ThrowCompletionOr < void > LeaveLexicalEnvironment : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
interpreter . vm ( ) . running_execution_context ( ) . lexical_environment = interpreter . saved_lexical_environment_stack ( ) . take_last ( ) ;
return { } ;
}
ThrowCompletionOr < void > LeaveUnwindContext : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
interpreter . leave_unwind_context ( ) ;
return { } ;
}
ThrowCompletionOr < void > ContinuePendingUnwind : : execute_impl ( Bytecode : : Interpreter & ) const
{
// Handled in the interpreter loop.
2023-09-27 11:31:23 +02:00
__builtin_unreachable ( ) ;
2023-09-27 10:10:00 +02:00
}
ThrowCompletionOr < void > PushDeclarativeEnvironment : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto environment = interpreter . vm ( ) . heap ( ) . allocate_without_realm < DeclarativeEnvironment > ( interpreter . vm ( ) . lexical_environment ( ) ) ;
interpreter . vm ( ) . running_execution_context ( ) . lexical_environment = environment ;
interpreter . vm ( ) . running_execution_context ( ) . variable_environment = environment ;
return { } ;
}
ThrowCompletionOr < void > Yield : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto yielded_value = interpreter . accumulator ( ) . value_or ( js_undefined ( ) ) ;
auto object = Object : : create ( interpreter . realm ( ) , nullptr ) ;
object - > define_direct_property ( " result " , yielded_value , JS : : default_attributes ) ;
if ( m_continuation_label . has_value ( ) )
// FIXME: If we get a pointer, which is not accurately representable as a double
// will cause this to explode
object - > define_direct_property ( " continuation " , Value ( static_cast < double > ( reinterpret_cast < u64 > ( & m_continuation_label - > block ( ) ) ) ) , JS : : default_attributes ) ;
else
object - > define_direct_property ( " continuation " , Value ( 0 ) , JS : : default_attributes ) ;
object - > define_direct_property ( " isAwait " , Value ( false ) , JS : : default_attributes ) ;
interpreter . do_return ( object ) ;
return { } ;
}
ThrowCompletionOr < void > Await : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto yielded_value = interpreter . accumulator ( ) . value_or ( js_undefined ( ) ) ;
auto object = Object : : create ( interpreter . realm ( ) , nullptr ) ;
object - > define_direct_property ( " result " , yielded_value , JS : : default_attributes ) ;
// FIXME: If we get a pointer, which is not accurately representable as a double
// will cause this to explode
object - > define_direct_property ( " continuation " , Value ( static_cast < double > ( reinterpret_cast < u64 > ( & m_continuation_label . block ( ) ) ) ) , JS : : default_attributes ) ;
object - > define_direct_property ( " isAwait " , Value ( true ) , JS : : default_attributes ) ;
interpreter . do_return ( object ) ;
return { } ;
}
ThrowCompletionOr < void > GetByValue : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
// NOTE: Get the property key from the accumulator before side effects have a chance to overwrite it.
auto property_key_value = interpreter . accumulator ( ) ;
auto base_value = interpreter . reg ( m_base ) ;
auto object = TRY ( base_object_for_get ( interpreter , base_value ) ) ;
2023-10-05 09:00:54 +02:00
// OPTIMIZATION: Fast path for simple Int32 indexes in array-like objects.
if ( property_key_value . is_int32 ( )
& & property_key_value . as_i32 ( ) > = 0
& & ! object - > may_interfere_with_indexed_property_access ( )
& & object - > indexed_properties ( ) . has_index ( property_key_value . as_i32 ( ) ) ) {
auto value = object - > indexed_properties ( ) . get ( property_key_value . as_i32 ( ) ) - > value ;
if ( ! value . is_accessor ( ) ) {
interpreter . accumulator ( ) = value ;
return { } ;
}
}
2023-09-27 10:10:00 +02:00
auto property_key = TRY ( property_key_value . to_property_key ( vm ) ) ;
if ( base_value . is_string ( ) ) {
auto string_value = TRY ( base_value . as_string ( ) . get ( vm , property_key ) ) ;
if ( string_value . has_value ( ) ) {
interpreter . accumulator ( ) = * string_value ;
return { } ;
}
}
interpreter . accumulator ( ) = TRY ( object - > internal_get ( property_key , base_value ) ) ;
return { } ;
}
ThrowCompletionOr < void > GetByValueWithThis : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
// NOTE: Get the property key from the accumulator before side effects have a chance to overwrite it.
auto property_key_value = interpreter . accumulator ( ) ;
auto object = TRY ( interpreter . reg ( m_base ) . to_object ( vm ) ) ;
auto property_key = TRY ( property_key_value . to_property_key ( vm ) ) ;
interpreter . accumulator ( ) = TRY ( object - > internal_get ( property_key , interpreter . reg ( m_this_value ) ) ) ;
return { } ;
}
ThrowCompletionOr < void > PutByValue : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
// NOTE: Get the value from the accumulator before side effects have a chance to overwrite it.
auto value = interpreter . accumulator ( ) ;
auto base = interpreter . reg ( m_base ) ;
2023-10-05 09:07:43 +02:00
auto property_key_value = interpreter . reg ( m_property ) ;
2023-09-27 10:10:00 +02:00
2023-10-05 09:07:43 +02:00
// OPTIMIZATION: Fast path for simple Int32 indexes in array-like objects.
if ( base . is_object ( ) & & property_key_value . is_int32 ( ) & & property_key_value . as_i32 ( ) > = 0 ) {
auto & object = base . as_object ( ) ;
auto * storage = object . indexed_properties ( ) . storage ( ) ;
auto index = static_cast < u32 > ( property_key_value . as_i32 ( ) ) ;
if ( storage
& & storage - > is_simple_storage ( )
& & ! object . may_interfere_with_indexed_property_access ( )
& & storage - > has_index ( index ) ) {
auto existing_value = storage - > get ( index ) - > value ;
if ( ! existing_value . is_accessor ( ) ) {
storage - > put ( index , value ) ;
interpreter . accumulator ( ) = value ;
return { } ;
}
}
}
auto property_key = m_kind ! = PropertyKind : : Spread ? TRY ( property_key_value . to_property_key ( vm ) ) : PropertyKey { } ;
2023-09-27 10:10:00 +02:00
TRY ( put_by_property_key ( vm , base , base , value , property_key , m_kind ) ) ;
interpreter . accumulator ( ) = value ;
return { } ;
}
ThrowCompletionOr < void > PutByValueWithThis : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
// NOTE: Get the value from the accumulator before side effects have a chance to overwrite it.
auto value = interpreter . accumulator ( ) ;
auto base = interpreter . reg ( m_base ) ;
auto property_key = m_kind ! = PropertyKind : : Spread ? TRY ( interpreter . reg ( m_property ) . to_property_key ( vm ) ) : PropertyKey { } ;
TRY ( put_by_property_key ( vm , base , interpreter . reg ( m_this_value ) , value , property_key , m_kind ) ) ;
interpreter . accumulator ( ) = value ;
return { } ;
}
ThrowCompletionOr < void > DeleteByValue : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
// NOTE: Get the property key from the accumulator before side effects have a chance to overwrite it.
auto property_key_value = interpreter . accumulator ( ) ;
auto base_value = interpreter . reg ( m_base ) ;
auto property_key = TRY ( property_key_value . to_property_key ( vm ) ) ;
bool strict = vm . in_strict_mode ( ) ;
auto reference = Reference { base_value , property_key , { } , strict } ;
interpreter . accumulator ( ) = Value ( TRY ( reference . delete_ ( vm ) ) ) ;
return { } ;
}
ThrowCompletionOr < void > DeleteByValueWithThis : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
// NOTE: Get the property key from the accumulator before side effects have a chance to overwrite it.
auto property_key_value = interpreter . accumulator ( ) ;
auto base_value = interpreter . reg ( m_base ) ;
auto property_key = TRY ( property_key_value . to_property_key ( vm ) ) ;
bool strict = vm . in_strict_mode ( ) ;
auto reference = Reference { base_value , property_key , interpreter . reg ( m_this_value ) , strict } ;
interpreter . accumulator ( ) = Value ( TRY ( reference . delete_ ( vm ) ) ) ;
return { } ;
}
ThrowCompletionOr < void > GetIterator : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto iterator = TRY ( get_iterator ( vm , interpreter . accumulator ( ) , m_hint ) ) ;
interpreter . accumulator ( ) = iterator_to_object ( vm , iterator ) ;
return { } ;
}
ThrowCompletionOr < void > GetMethod : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto identifier = interpreter . current_executable ( ) . get_identifier ( m_property ) ;
auto method = TRY ( interpreter . accumulator ( ) . get_method ( vm , identifier ) ) ;
interpreter . accumulator ( ) = method ? : js_undefined ( ) ;
return { } ;
}
// 14.7.5.9 EnumerateObjectProperties ( O ), https://tc39.es/ecma262/#sec-enumerate-object-properties
ThrowCompletionOr < void > GetObjectPropertyIterator : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
// While the spec does provide an algorithm, it allows us to implement it ourselves so long as we meet the following invariants:
// 1- Returned property keys do not include keys that are Symbols
// 2- Properties of the target object may be deleted during enumeration. A property that is deleted before it is processed by the iterator's next method is ignored
// 3- If new properties are added to the target object during enumeration, the newly added properties are not guaranteed to be processed in the active enumeration
// 4- A property name will be returned by the iterator's next method at most once in any enumeration.
// 5- Enumerating the properties of the target object includes enumerating properties of its prototype, and the prototype of the prototype, and so on, recursively;
// but a property of a prototype is not processed if it has the same name as a property that has already been processed by the iterator's next method.
// 6- The values of [[Enumerable]] attributes are not considered when determining if a property of a prototype object has already been processed.
// 7- The enumerable property names of prototype objects must be obtained by invoking EnumerateObjectProperties passing the prototype object as the argument.
// 8- EnumerateObjectProperties must obtain the own property keys of the target object by calling its [[OwnPropertyKeys]] internal method.
// 9- Property attributes of the target object must be obtained by calling its [[GetOwnProperty]] internal method
// Invariant 3 effectively allows the implementation to ignore newly added keys, and we do so (similar to other implementations).
auto & vm = interpreter . vm ( ) ;
auto object = TRY ( interpreter . accumulator ( ) . to_object ( vm ) ) ;
// Note: While the spec doesn't explicitly require these to be ordered, it says that the values should be retrieved via OwnPropertyKeys,
// so we just keep the order consistent anyway.
OrderedHashTable < PropertyKey > properties ;
OrderedHashTable < PropertyKey > non_enumerable_properties ;
HashTable < NonnullGCPtr < Object > > seen_objects ;
// Collect all keys immediately (invariant no. 5)
for ( auto object_to_check = GCPtr { object . ptr ( ) } ; object_to_check & & ! seen_objects . contains ( * object_to_check ) ; object_to_check = TRY ( object_to_check - > internal_get_prototype_of ( ) ) ) {
seen_objects . set ( * object_to_check ) ;
for ( auto & key : TRY ( object_to_check - > internal_own_property_keys ( ) ) ) {
if ( key . is_symbol ( ) )
continue ;
auto property_key = TRY ( PropertyKey : : from_value ( vm , key ) ) ;
// If there is a non-enumerable property higher up the prototype chain with the same key,
// we mustn't include this property even if it's enumerable (invariant no. 5 and 6)
if ( non_enumerable_properties . contains ( property_key ) )
continue ;
if ( properties . contains ( property_key ) )
continue ;
auto descriptor = TRY ( object_to_check - > internal_get_own_property ( property_key ) ) ;
if ( ! * descriptor - > enumerable )
non_enumerable_properties . set ( move ( property_key ) ) ;
else
properties . set ( move ( property_key ) ) ;
}
}
IteratorRecord iterator {
. iterator = object ,
. next_method = NativeFunction : : create (
interpreter . realm ( ) ,
[ items = move ( properties ) ] ( VM & vm ) mutable - > ThrowCompletionOr < Value > {
auto & realm = * vm . current_realm ( ) ;
auto iterated_object_value = vm . this_value ( ) ;
if ( ! iterated_object_value . is_object ( ) )
return vm . throw_completion < InternalError > ( " Invalid state for GetObjectPropertyIterator.next " sv ) ;
auto & iterated_object = iterated_object_value . as_object ( ) ;
auto result_object = Object : : create ( realm , nullptr ) ;
while ( true ) {
if ( items . is_empty ( ) ) {
result_object - > define_direct_property ( vm . names . done , JS : : Value ( true ) , default_attributes ) ;
return result_object ;
}
auto key = items . take_first ( ) ;
// If the property is deleted, don't include it (invariant no. 2)
if ( ! TRY ( iterated_object . has_property ( key ) ) )
continue ;
result_object - > define_direct_property ( vm . names . done , JS : : Value ( false ) , default_attributes ) ;
if ( key . is_number ( ) )
result_object - > define_direct_property ( vm . names . value , PrimitiveString : : create ( vm , TRY_OR_THROW_OOM ( vm , String : : number ( key . as_number ( ) ) ) ) , default_attributes ) ;
else if ( key . is_string ( ) )
result_object - > define_direct_property ( vm . names . value , PrimitiveString : : create ( vm , key . as_string ( ) ) , default_attributes ) ;
else
VERIFY_NOT_REACHED ( ) ; // We should not have non-string/number keys.
return result_object ;
}
} ,
1 ,
vm . names . next ) ,
. done = false ,
} ;
interpreter . accumulator ( ) = iterator_to_object ( vm , move ( iterator ) ) ;
return { } ;
}
ThrowCompletionOr < void > IteratorClose : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto iterator_object = TRY ( interpreter . accumulator ( ) . to_object ( vm ) ) ;
auto iterator = object_to_iterator ( vm , iterator_object ) ;
// FIXME: Return the value of the resulting completion. (Note that m_completion_value can be empty!)
TRY ( iterator_close ( vm , iterator , Completion { m_completion_type , m_completion_value , { } } ) ) ;
return { } ;
}
ThrowCompletionOr < void > AsyncIteratorClose : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto iterator_object = TRY ( interpreter . accumulator ( ) . to_object ( vm ) ) ;
auto iterator = object_to_iterator ( vm , iterator_object ) ;
// FIXME: Return the value of the resulting completion. (Note that m_completion_value can be empty!)
TRY ( async_iterator_close ( vm , iterator , Completion { m_completion_type , m_completion_value , { } } ) ) ;
return { } ;
}
ThrowCompletionOr < void > IteratorNext : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto iterator_object = TRY ( interpreter . accumulator ( ) . to_object ( vm ) ) ;
auto iterator = object_to_iterator ( vm , iterator_object ) ;
interpreter . accumulator ( ) = TRY ( iterator_next ( vm , iterator ) ) ;
return { } ;
}
ThrowCompletionOr < void > IteratorResultDone : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto iterator_result = TRY ( interpreter . accumulator ( ) . to_object ( vm ) ) ;
auto complete = TRY ( iterator_complete ( vm , iterator_result ) ) ;
interpreter . accumulator ( ) = Value ( complete ) ;
return { } ;
}
ThrowCompletionOr < void > IteratorResultValue : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto iterator_result = TRY ( interpreter . accumulator ( ) . to_object ( vm ) ) ;
interpreter . accumulator ( ) = TRY ( iterator_value ( vm , iterator_result ) ) ;
return { } ;
}
ThrowCompletionOr < void > NewClass : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto name = m_class_expression . name ( ) ;
auto super_class = interpreter . accumulator ( ) ;
// NOTE: NewClass expects classEnv to be active lexical environment
auto class_environment = vm . lexical_environment ( ) ;
vm . running_execution_context ( ) . lexical_environment = interpreter . saved_lexical_environment_stack ( ) . take_last ( ) ;
DeprecatedFlyString binding_name ;
DeprecatedFlyString class_name ;
if ( ! m_class_expression . has_name ( ) & & m_lhs_name . has_value ( ) ) {
class_name = interpreter . current_executable ( ) . get_identifier ( m_lhs_name . value ( ) ) ;
} else {
binding_name = name ;
class_name = name . is_null ( ) ? " " sv : name ;
}
interpreter . accumulator ( ) = TRY ( m_class_expression . create_class_constructor ( vm , class_environment , vm . lexical_environment ( ) , super_class , binding_name , class_name ) ) ;
return { } ;
}
// 13.5.3.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-typeof-operator-runtime-semantics-evaluation
ThrowCompletionOr < void > TypeofVariable : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
// 1. Let val be the result of evaluating UnaryExpression.
auto const & string = interpreter . current_executable ( ) . get_identifier ( m_identifier ) ;
auto reference = TRY ( vm . resolve_binding ( string ) ) ;
// 2. If val is a Reference Record, then
// a. If IsUnresolvableReference(val) is true, return "undefined".
if ( reference . is_unresolvable ( ) ) {
interpreter . accumulator ( ) = PrimitiveString : : create ( vm , " undefined " _string ) ;
return { } ;
}
// 3. Set val to ? GetValue(val).
auto value = TRY ( reference . get_value ( vm ) ) ;
// 4. NOTE: This step is replaced in section B.3.6.3.
// 5. Return a String according to Table 41.
interpreter . accumulator ( ) = PrimitiveString : : create ( vm , value . typeof ( ) ) ;
return { } ;
}
ThrowCompletionOr < void > TypeofLocal : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto const & value = vm . running_execution_context ( ) . local_variables [ m_index ] ;
interpreter . accumulator ( ) = PrimitiveString : : create ( vm , value . typeof ( ) ) ;
return { } ;
}
ThrowCompletionOr < void > ToNumeric : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
interpreter . accumulator ( ) = TRY ( interpreter . accumulator ( ) . to_numeric ( interpreter . vm ( ) ) ) ;
return { } ;
}
ThrowCompletionOr < void > BlockDeclarationInstantiation : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto old_environment = vm . running_execution_context ( ) . lexical_environment ;
interpreter . saved_lexical_environment_stack ( ) . append ( old_environment ) ;
vm . running_execution_context ( ) . lexical_environment = new_declarative_environment ( * old_environment ) ;
m_scope_node . block_declaration_instantiation ( vm , vm . running_execution_context ( ) . lexical_environment ) ;
return { } ;
}
DeprecatedString Load : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return DeprecatedString : : formatted ( " Load {} " , m_src ) ;
}
DeprecatedString LoadImmediate : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return DeprecatedString : : formatted ( " LoadImmediate {} " , m_value ) ;
}
DeprecatedString Store : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return DeprecatedString : : formatted ( " Store {} " , m_dst ) ;
}
DeprecatedString NewBigInt : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return DeprecatedString : : formatted ( " NewBigInt \" {} \" " , m_bigint . to_base_deprecated ( 10 ) ) ;
}
DeprecatedString NewArray : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
StringBuilder builder ;
builder . append ( " NewArray " sv ) ;
if ( m_element_count ! = 0 ) {
builder . appendff ( " [{}-{}] " , m_elements [ 0 ] , m_elements [ 1 ] ) ;
}
return builder . to_deprecated_string ( ) ;
}
DeprecatedString Append : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
if ( m_is_spread )
return DeprecatedString : : formatted ( " Append lhs: **{} " , m_lhs ) ;
return DeprecatedString : : formatted ( " Append lhs: {} " , m_lhs ) ;
}
DeprecatedString IteratorToArray : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " IteratorToArray " ;
}
DeprecatedString NewString : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
return DeprecatedString : : formatted ( " NewString {} ( \" {} \" ) " , m_string , executable . string_table - > get ( m_string ) ) ;
}
DeprecatedString NewObject : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " NewObject " ;
}
DeprecatedString NewRegExp : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
return DeprecatedString : : formatted ( " NewRegExp source:{} ( \" {} \" ) flags:{} ( \" {} \" ) " , m_source_index , executable . get_string ( m_source_index ) , m_flags_index , executable . get_string ( m_flags_index ) ) ;
}
DeprecatedString CopyObjectExcludingProperties : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
StringBuilder builder ;
builder . appendff ( " CopyObjectExcludingProperties from:{} " , m_from_object ) ;
if ( m_excluded_names_count ! = 0 ) {
builder . append ( " excluding:[ " sv ) ;
builder . join ( " , " sv , ReadonlySpan < Register > ( m_excluded_names , m_excluded_names_count ) ) ;
builder . append ( ' ] ' ) ;
}
return builder . to_deprecated_string ( ) ;
}
DeprecatedString ConcatString : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return DeprecatedString : : formatted ( " ConcatString {} " , m_lhs ) ;
}
DeprecatedString GetCalleeAndThisFromEnvironment : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
return DeprecatedString : : formatted ( " GetCalleeAndThisFromEnvironment {} -> callee: {}, this:{} " , executable . identifier_table - > get ( m_identifier ) , m_callee_reg , m_this_reg ) ;
}
DeprecatedString GetVariable : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
return DeprecatedString : : formatted ( " GetVariable {} ({}) " , m_identifier , executable . identifier_table - > get ( m_identifier ) ) ;
}
DeprecatedString GetGlobal : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
return DeprecatedString : : formatted ( " GetGlobal {} ({}) " , m_identifier , executable . identifier_table - > get ( m_identifier ) ) ;
}
DeprecatedString GetLocal : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return DeprecatedString : : formatted ( " GetLocal {} " , m_index ) ;
}
DeprecatedString DeleteVariable : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
return DeprecatedString : : formatted ( " DeleteVariable {} ({}) " , m_identifier , executable . identifier_table - > get ( m_identifier ) ) ;
}
DeprecatedString CreateLexicalEnvironment : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " CreateLexicalEnvironment " sv ;
}
DeprecatedString CreateVariable : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
auto mode_string = m_mode = = EnvironmentMode : : Lexical ? " Lexical " : " Variable " ;
return DeprecatedString : : formatted ( " CreateVariable env:{} immutable:{} global:{} {} ({}) " , mode_string , m_is_immutable , m_is_global , m_identifier , executable . identifier_table - > get ( m_identifier ) ) ;
}
DeprecatedString EnterObjectEnvironment : : to_deprecated_string_impl ( Executable const & ) const
{
return DeprecatedString : : formatted ( " EnterObjectEnvironment " ) ;
}
DeprecatedString SetVariable : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
auto initialization_mode_name = m_initialization_mode = = InitializationMode : : Initialize ? " Initialize " : " Set " ;
auto mode_string = m_mode = = EnvironmentMode : : Lexical ? " Lexical " : " Variable " ;
return DeprecatedString : : formatted ( " SetVariable env:{} init:{} {} ({}) " , mode_string , initialization_mode_name , m_identifier , executable . identifier_table - > get ( m_identifier ) ) ;
}
DeprecatedString SetLocal : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return DeprecatedString : : formatted ( " SetLocal {} " , m_index ) ;
}
static StringView property_kind_to_string ( PropertyKind kind )
{
switch ( kind ) {
case PropertyKind : : Getter :
return " getter " sv ;
case PropertyKind : : Setter :
return " setter " sv ;
case PropertyKind : : KeyValue :
return " key-value " sv ;
case PropertyKind : : DirectKeyValue :
return " direct-key-value " sv ;
case PropertyKind : : Spread :
return " spread " sv ;
case PropertyKind : : ProtoSetter :
return " proto-setter " sv ;
}
VERIFY_NOT_REACHED ( ) ;
}
DeprecatedString PutById : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
auto kind = property_kind_to_string ( m_kind ) ;
return DeprecatedString : : formatted ( " PutById kind:{} base:{}, property:{} ({}) " , kind , m_base , m_property , executable . identifier_table - > get ( m_property ) ) ;
}
DeprecatedString PutByIdWithThis : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
auto kind = property_kind_to_string ( m_kind ) ;
return DeprecatedString : : formatted ( " PutByIdWithThis kind:{} base:{}, property:{} ({}) this_value:{} " , kind , m_base , m_property , executable . identifier_table - > get ( m_property ) , m_this_value ) ;
}
DeprecatedString PutPrivateById : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
auto kind = property_kind_to_string ( m_kind ) ;
return DeprecatedString : : formatted ( " PutPrivateById kind:{} base:{}, property:{} ({}) " , kind , m_base , m_property , executable . identifier_table - > get ( m_property ) ) ;
}
DeprecatedString GetById : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
return DeprecatedString : : formatted ( " GetById {} ({}) " , m_property , executable . identifier_table - > get ( m_property ) ) ;
}
DeprecatedString GetByIdWithThis : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
return DeprecatedString : : formatted ( " GetByIdWithThis {} ({}) this_value:{} " , m_property , executable . identifier_table - > get ( m_property ) , m_this_value ) ;
}
DeprecatedString GetPrivateById : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
return DeprecatedString : : formatted ( " GetPrivateById {} ({}) " , m_property , executable . identifier_table - > get ( m_property ) ) ;
}
DeprecatedString HasPrivateId : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
return DeprecatedString : : formatted ( " HasPrivateId {} ({}) " , m_property , executable . identifier_table - > get ( m_property ) ) ;
}
DeprecatedString DeleteById : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
return DeprecatedString : : formatted ( " DeleteById {} ({}) " , m_property , executable . identifier_table - > get ( m_property ) ) ;
}
DeprecatedString DeleteByIdWithThis : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
return DeprecatedString : : formatted ( " DeleteByIdWithThis {} ({}) this_value:{} " , m_property , executable . identifier_table - > get ( m_property ) , m_this_value ) ;
}
DeprecatedString Jump : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
if ( m_true_target . has_value ( ) )
return DeprecatedString : : formatted ( " Jump {} " , * m_true_target ) ;
return DeprecatedString : : formatted ( " Jump <empty> " ) ;
}
DeprecatedString JumpConditional : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
auto true_string = m_true_target . has_value ( ) ? DeprecatedString : : formatted ( " {} " , * m_true_target ) : " <empty> " ;
auto false_string = m_false_target . has_value ( ) ? DeprecatedString : : formatted ( " {} " , * m_false_target ) : " <empty> " ;
return DeprecatedString : : formatted ( " JumpConditional true:{} false:{} " , true_string , false_string ) ;
}
DeprecatedString JumpNullish : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
auto true_string = m_true_target . has_value ( ) ? DeprecatedString : : formatted ( " {} " , * m_true_target ) : " <empty> " ;
auto false_string = m_false_target . has_value ( ) ? DeprecatedString : : formatted ( " {} " , * m_false_target ) : " <empty> " ;
return DeprecatedString : : formatted ( " JumpNullish null:{} nonnull:{} " , true_string , false_string ) ;
}
DeprecatedString JumpUndefined : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
auto true_string = m_true_target . has_value ( ) ? DeprecatedString : : formatted ( " {} " , * m_true_target ) : " <empty> " ;
auto false_string = m_false_target . has_value ( ) ? DeprecatedString : : formatted ( " {} " , * m_false_target ) : " <empty> " ;
return DeprecatedString : : formatted ( " JumpUndefined undefined:{} not undefined:{} " , true_string , false_string ) ;
}
static StringView call_type_to_string ( CallType type )
{
switch ( type ) {
case CallType : : Call :
return " " sv ;
case CallType : : Construct :
return " (Construct) " sv ;
case CallType : : DirectEval :
return " (DirectEval) " sv ;
}
VERIFY_NOT_REACHED ( ) ;
}
DeprecatedString Call : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
auto type = call_type_to_string ( m_type ) ;
if ( m_expression_string . has_value ( ) )
return DeprecatedString : : formatted ( " Call{} callee:{}, this:{}, first_arg:{} ({}) " , type , m_callee , m_this_value , m_first_argument , executable . get_string ( m_expression_string . value ( ) ) ) ;
return DeprecatedString : : formatted ( " Call{} callee:{}, this:{}, first_arg:{} " , type , m_callee , m_first_argument , m_this_value ) ;
}
DeprecatedString CallWithArgumentArray : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
auto type = call_type_to_string ( m_type ) ;
if ( m_expression_string . has_value ( ) )
return DeprecatedString : : formatted ( " CallWithArgumentArray{} callee:{}, this:{}, arguments:[...acc] ({}) " , type , m_callee , m_this_value , executable . get_string ( m_expression_string . value ( ) ) ) ;
return DeprecatedString : : formatted ( " CallWithArgumentArray{} callee:{}, this:{}, arguments:[...acc] " , type , m_callee , m_this_value ) ;
}
DeprecatedString SuperCallWithArgumentArray : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " SuperCallWithArgumentArray arguments:[...acc] " sv ;
}
DeprecatedString NewFunction : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
StringBuilder builder ;
builder . append ( " NewFunction " sv ) ;
if ( m_function_node . has_name ( ) )
builder . appendff ( " name:{} " sv , m_function_node . name ( ) ) ;
if ( m_lhs_name . has_value ( ) )
builder . appendff ( " lhs_name:{} " sv , m_lhs_name . value ( ) ) ;
if ( m_home_object . has_value ( ) )
builder . appendff ( " home_object:{} " sv , m_home_object . value ( ) ) ;
return builder . to_deprecated_string ( ) ;
}
DeprecatedString NewClass : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
StringBuilder builder ;
auto name = m_class_expression . name ( ) ;
builder . appendff ( " NewClass '{}' " sv , name . is_null ( ) ? " " sv : name ) ;
if ( m_lhs_name . has_value ( ) )
builder . appendff ( " lhs_name:{} " sv , m_lhs_name . value ( ) ) ;
return builder . to_deprecated_string ( ) ;
}
DeprecatedString Return : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " Return " ;
}
DeprecatedString Increment : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " Increment " ;
}
DeprecatedString Decrement : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " Decrement " ;
}
DeprecatedString Throw : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " Throw " ;
}
DeprecatedString ThrowIfNotObject : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " ThrowIfNotObject " ;
}
DeprecatedString ThrowIfNullish : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " ThrowIfNullish " ;
}
DeprecatedString EnterUnwindContext : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
auto handler_string = m_handler_target . has_value ( ) ? DeprecatedString : : formatted ( " {} " , * m_handler_target ) : " <empty> " ;
auto finalizer_string = m_finalizer_target . has_value ( ) ? DeprecatedString : : formatted ( " {} " , * m_finalizer_target ) : " <empty> " ;
return DeprecatedString : : formatted ( " EnterUnwindContext handler:{} finalizer:{} entry:{} " , handler_string , finalizer_string , m_entry_point ) ;
}
DeprecatedString ScheduleJump : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return DeprecatedString : : formatted ( " ScheduleJump {} " , m_target ) ;
}
DeprecatedString LeaveLexicalEnvironment : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " LeaveLexicalEnvironment " sv ;
}
DeprecatedString LeaveUnwindContext : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " LeaveUnwindContext " ;
}
DeprecatedString ContinuePendingUnwind : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return DeprecatedString : : formatted ( " ContinuePendingUnwind resume:{} " , m_resume_target ) ;
}
DeprecatedString PushDeclarativeEnvironment : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
StringBuilder builder ;
builder . append ( " PushDeclarativeEnvironment " sv ) ;
if ( ! m_variables . is_empty ( ) ) {
builder . append ( " { " sv ) ;
Vector < DeprecatedString > names ;
for ( auto & it : m_variables )
names . append ( executable . get_string ( it . key ) ) ;
builder . append ( ' } ' ) ;
builder . join ( " , " sv , names ) ;
}
return builder . to_deprecated_string ( ) ;
}
DeprecatedString Yield : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
if ( m_continuation_label . has_value ( ) )
return DeprecatedString : : formatted ( " Yield continuation:@{} " , m_continuation_label - > block ( ) . name ( ) ) ;
return DeprecatedString : : formatted ( " Yield return " ) ;
}
DeprecatedString Await : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return DeprecatedString : : formatted ( " Await continuation:@{} " , m_continuation_label . block ( ) . name ( ) ) ;
}
DeprecatedString GetByValue : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return DeprecatedString : : formatted ( " GetByValue base:{} " , m_base ) ;
}
DeprecatedString GetByValueWithThis : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return DeprecatedString : : formatted ( " GetByValueWithThis base:{} this_value:{} " , m_base , m_this_value ) ;
}
DeprecatedString PutByValue : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
auto kind = property_kind_to_string ( m_kind ) ;
return DeprecatedString : : formatted ( " PutByValue kind:{} base:{}, property:{} " , kind , m_base , m_property ) ;
}
DeprecatedString PutByValueWithThis : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
auto kind = property_kind_to_string ( m_kind ) ;
return DeprecatedString : : formatted ( " PutByValueWithThis kind:{} base:{}, property:{} this_value:{} " , kind , m_base , m_property , m_this_value ) ;
}
DeprecatedString DeleteByValue : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return DeprecatedString : : formatted ( " DeleteByValue base:{} " , m_base ) ;
}
DeprecatedString DeleteByValueWithThis : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return DeprecatedString : : formatted ( " DeleteByValueWithThis base:{} this_value:{} " , m_base , m_this_value ) ;
}
DeprecatedString GetIterator : : to_deprecated_string_impl ( Executable const & ) const
{
auto hint = m_hint = = IteratorHint : : Sync ? " sync " : " async " ;
return DeprecatedString : : formatted ( " GetIterator hint:{} " , hint ) ;
}
DeprecatedString GetMethod : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
return DeprecatedString : : formatted ( " GetMethod {} ({}) " , m_property , executable . identifier_table - > get ( m_property ) ) ;
}
DeprecatedString GetObjectPropertyIterator : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " GetObjectPropertyIterator " ;
}
DeprecatedString IteratorClose : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
if ( ! m_completion_value . has_value ( ) )
return DeprecatedString : : formatted ( " IteratorClose completion_type={} completion_value=<empty> " , to_underlying ( m_completion_type ) ) ;
auto completion_value_string = m_completion_value - > to_string_without_side_effects ( ) ;
return DeprecatedString : : formatted ( " IteratorClose completion_type={} completion_value={} " , to_underlying ( m_completion_type ) , completion_value_string ) ;
}
DeprecatedString AsyncIteratorClose : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
if ( ! m_completion_value . has_value ( ) )
return DeprecatedString : : formatted ( " AsyncIteratorClose completion_type={} completion_value=<empty> " , to_underlying ( m_completion_type ) ) ;
auto completion_value_string = m_completion_value - > to_string_without_side_effects ( ) ;
return DeprecatedString : : formatted ( " AsyncIteratorClose completion_type={} completion_value={} " , to_underlying ( m_completion_type ) , completion_value_string ) ;
}
DeprecatedString IteratorNext : : to_deprecated_string_impl ( Executable const & ) const
{
return " IteratorNext " ;
}
DeprecatedString IteratorResultDone : : to_deprecated_string_impl ( Executable const & ) const
{
return " IteratorResultDone " ;
}
DeprecatedString IteratorResultValue : : to_deprecated_string_impl ( Executable const & ) const
{
return " IteratorResultValue " ;
}
DeprecatedString ResolveThisBinding : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " ResolveThisBinding " sv ;
}
DeprecatedString ResolveSuperBase : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " ResolveSuperBase " sv ;
}
DeprecatedString GetNewTarget : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " GetNewTarget " sv ;
}
DeprecatedString GetImportMeta : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " GetImportMeta " sv ;
}
DeprecatedString TypeofVariable : : to_deprecated_string_impl ( Bytecode : : Executable const & executable ) const
{
return DeprecatedString : : formatted ( " TypeofVariable {} ({}) " , m_identifier , executable . identifier_table - > get ( m_identifier ) ) ;
}
DeprecatedString TypeofLocal : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return DeprecatedString : : formatted ( " TypeofLocal {} " , m_index ) ;
}
DeprecatedString ToNumeric : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " ToNumeric " sv ;
}
DeprecatedString BlockDeclarationInstantiation : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return " BlockDeclarationInstantiation " sv ;
}
DeprecatedString ImportCall : : to_deprecated_string_impl ( Bytecode : : Executable const & ) const
{
return DeprecatedString : : formatted ( " ImportCall specifier:{} options:{} " sv , m_specifier , m_options ) ;
}
}