2021-06-03 10:46:30 +02:00
/*
2025-03-16 17:41:24 -05:00
* Copyright ( c ) 2021 - 2025 , Andreas Kling < andreas @ ladybird . org >
2025-04-30 16:31:26 +03:00
* Copyright ( c ) 2025 , Aliaksandr Kalenik < kalenik . aliaksandr @ gmail . com >
2021-06-03 10:46:30 +02:00
*
* 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>
2025-05-03 11:35:22 +02:00
# include <LibGC/RootHashMap.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>
2023-10-20 00:33:51 +02:00
# include <LibJS/Bytecode/Label.h>
2021-06-09 09:19:34 +02:00
# include <LibJS/Bytecode/Op.h>
2025-05-24 23:46:13 +02:00
# include <LibJS/Export.h>
2023-09-27 10:10:00 +02:00
# include <LibJS/Runtime/AbstractOperations.h>
2024-10-17 20:29:07 +02:00
# include <LibJS/Runtime/Accessor.h>
2023-09-27 10:10:00 +02:00
# include <LibJS/Runtime/Array.h>
# include <LibJS/Runtime/BigInt.h>
2025-03-31 09:32:39 +01:00
# include <LibJS/Runtime/CompletionCell.h>
2023-09-27 10:10:00 +02:00
# include <LibJS/Runtime/DeclarativeEnvironment.h>
# include <LibJS/Runtime/ECMAScriptFunctionObject.h>
# include <LibJS/Runtime/Environment.h>
# include <LibJS/Runtime/FunctionEnvironment.h>
2025-03-31 09:32:39 +01:00
# include <LibJS/Runtime/GeneratorResult.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>
2023-11-30 19:49:29 +01:00
# include <LibJS/Runtime/MathObject.h>
2025-03-16 17:41:24 -05:00
# include <LibJS/Runtime/ModuleEnvironment.h>
2023-09-27 10:10:00 +02:00
# 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>
2024-07-09 10:10:14 +02:00
# include <LibJS/Runtime/TypedArray.h>
2023-09-27 10:10:00 +02:00
# 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
2024-02-04 08:00:54 +01:00
static ByteString format_operand ( StringView name , Operand operand , Bytecode : : Executable const & executable )
{
StringBuilder builder ;
2024-02-20 15:59:45 +01:00
if ( ! name . is_empty ( ) )
builder . appendff ( " \033 [32m{} \033 [0m: " , name ) ;
2024-02-04 08:00:54 +01:00
switch ( operand . type ( ) ) {
case Operand : : Type : : Register :
2024-05-31 20:59:55 +02:00
if ( operand . index ( ) = = Register : : this_value ( ) . index ( ) ) {
builder . appendff ( " \033 [33mthis \033 [0m " ) ;
} else {
builder . appendff ( " \033 [33mreg{} \033 [0m " , operand . index ( ) ) ;
}
2024-02-04 08:00:54 +01:00
break ;
case Operand : : Type : : Local :
2025-05-05 21:53:19 +03:00
builder . appendff ( " \033 [34m{}~{} \033 [0m " , executable . local_variable_names [ operand . index ( ) - executable . local_index_base ] . name , operand . index ( ) - executable . local_index_base ) ;
2024-02-04 08:00:54 +01:00
break ;
2025-04-24 22:10:41 +02:00
case Operand : : Type : : Argument :
builder . appendff ( " \033 [34marg{} \033 [0m " , operand . index ( ) - executable . argument_index_base ) ;
break ;
2024-02-04 08:00:54 +01:00
case Operand : : Type : : Constant : {
builder . append ( " \033 [36m " sv ) ;
2024-05-12 18:49:03 +02:00
auto value = executable . constants [ operand . index ( ) - executable . number_of_registers ] ;
2025-04-04 23:16:34 +02:00
if ( value . is_special_empty_value ( ) )
2024-02-04 08:00:54 +01:00
builder . append ( " <Empty> " sv ) ;
else if ( value . is_boolean ( ) )
builder . appendff ( " Bool({}) " , value . as_bool ( ) ? " true " sv : " false " sv ) ;
else if ( value . is_int32 ( ) )
builder . appendff ( " Int32({}) " , value . as_i32 ( ) ) ;
else if ( value . is_double ( ) )
builder . appendff ( " Double({}) " , value . as_double ( ) ) ;
2024-03-03 11:40:00 +01:00
else if ( value . is_bigint ( ) )
2025-08-07 16:33:10 -04:00
builder . appendff ( " BigInt({}) " , MUST ( value . as_bigint ( ) . to_string ( ) ) ) ;
2024-03-03 11:34:36 +01:00
else if ( value . is_string ( ) )
builder . appendff ( " String( \" {} \" ) " , value . as_string ( ) . utf8_string_view ( ) ) ;
2024-02-04 08:00:54 +01:00
else if ( value . is_undefined ( ) )
builder . append ( " Undefined " sv ) ;
else if ( value . is_null ( ) )
builder . append ( " Null " sv ) ;
else
builder . appendff ( " Value: {} " , value ) ;
builder . append ( " \033 [0m " sv ) ;
break ;
}
default :
VERIFY_NOT_REACHED ( ) ;
}
return builder . to_byte_string ( ) ;
}
2024-02-20 15:59:45 +01:00
static ByteString format_operand_list ( StringView name , ReadonlySpan < Operand > operands , Bytecode : : Executable const & executable )
{
StringBuilder builder ;
if ( ! name . is_empty ( ) )
2024-05-08 12:43:08 +02:00
builder . appendff ( " \033 [32m{} \033 [0m:[ " , name ) ;
2024-02-20 15:59:45 +01:00
for ( size_t i = 0 ; i < operands . size ( ) ; + + i ) {
if ( i ! = 0 )
builder . append ( " , " sv ) ;
builder . appendff ( " {} " , format_operand ( " " sv , operands [ i ] , executable ) ) ;
}
builder . append ( " ] " sv ) ;
return builder . to_byte_string ( ) ;
}
2024-03-03 12:37:28 +01:00
static ByteString format_value_list ( StringView name , ReadonlySpan < Value > values )
{
StringBuilder builder ;
if ( ! name . is_empty ( ) )
2024-05-06 13:50:21 +02:00
builder . appendff ( " \033 [32m{} \033 [0m:[ " , name ) ;
2024-03-03 12:37:28 +01:00
builder . join ( " , " sv , values ) ;
builder . append ( " ] " sv ) ;
return builder . to_byte_string ( ) ;
}
2025-05-08 15:19:35 +12:00
ALWAYS_INLINE static ThrowCompletionOr < bool > loosely_inequals ( VM & vm , Value src1 , Value src2 )
2024-03-03 14:56:33 +01:00
{
2024-03-04 09:34:30 +01:00
if ( src1 . tag ( ) = = src2 . tag ( ) ) {
if ( src1 . is_int32 ( ) | | src1 . is_object ( ) | | src1 . is_boolean ( ) | | src1 . is_nullish ( ) )
2025-05-08 15:19:35 +12:00
return src1 . encoded ( ) ! = src2 . encoded ( ) ;
2024-03-04 09:34:30 +01:00
}
2025-05-08 15:19:35 +12:00
return ! TRY ( is_loosely_equal ( vm , src1 , src2 ) ) ;
2024-03-03 14:56:33 +01:00
}
2025-05-08 15:19:35 +12:00
ALWAYS_INLINE static ThrowCompletionOr < bool > loosely_equals ( VM & vm , Value src1 , Value src2 )
2024-03-03 14:56:33 +01:00
{
2024-03-04 09:34:30 +01:00
if ( src1 . tag ( ) = = src2 . tag ( ) ) {
if ( src1 . is_int32 ( ) | | src1 . is_object ( ) | | src1 . is_boolean ( ) | | src1 . is_nullish ( ) )
2025-05-08 15:19:35 +12:00
return src1 . encoded ( ) = = src2 . encoded ( ) ;
2024-03-04 09:34:30 +01:00
}
2025-05-08 15:19:35 +12:00
return TRY ( is_loosely_equal ( vm , src1 , src2 ) ) ;
2024-03-03 14:56:33 +01:00
}
2025-05-08 15:19:35 +12:00
ALWAYS_INLINE static ThrowCompletionOr < bool > strict_inequals ( VM & , Value src1 , Value src2 )
2024-03-03 14:56:33 +01:00
{
2024-03-04 09:34:30 +01:00
if ( src1 . tag ( ) = = src2 . tag ( ) ) {
if ( src1 . is_int32 ( ) | | src1 . is_object ( ) | | src1 . is_boolean ( ) | | src1 . is_nullish ( ) )
2025-05-08 15:19:35 +12:00
return src1 . encoded ( ) ! = src2 . encoded ( ) ;
2024-03-04 09:34:30 +01:00
}
2025-05-08 15:19:35 +12:00
return ! is_strictly_equal ( src1 , src2 ) ;
2024-03-03 14:56:33 +01:00
}
2025-05-08 15:19:35 +12:00
ALWAYS_INLINE static ThrowCompletionOr < bool > strict_equals ( VM & , Value src1 , Value src2 )
2024-03-03 14:56:33 +01:00
{
2024-03-04 09:34:30 +01:00
if ( src1 . tag ( ) = = src2 . tag ( ) ) {
if ( src1 . is_int32 ( ) | | src1 . is_object ( ) | | src1 . is_boolean ( ) | | src1 . is_nullish ( ) )
2025-05-08 15:19:35 +12:00
return src1 . encoded ( ) = = src2 . encoded ( ) ;
2024-03-04 09:34:30 +01:00
}
2025-05-08 15:19:35 +12:00
return is_strictly_equal ( src1 , src2 ) ;
2024-03-03 14:56:33 +01: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 ( )
{
}
2025-04-29 16:08:42 +02:00
ALWAYS_INLINE Value Interpreter : : get ( Operand op ) const
{
return m_registers_and_constants_and_locals_arguments . data ( ) [ op . index ( ) ] ;
}
ALWAYS_INLINE void Interpreter : : set ( Operand op , Value value )
{
m_registers_and_constants_and_locals_arguments . data ( ) [ op . index ( ) ] = value ;
}
2024-05-18 17:25:43 +02:00
ALWAYS_INLINE Value Interpreter : : do_yield ( Value value , Optional < Label > continuation )
{
2025-03-31 09:32:39 +01:00
// FIXME: If we get a pointer, which is not accurately representable as a double
// will cause this to explode
auto continuation_value = continuation . has_value ( ) ? Value ( continuation - > address ( ) ) : js_null ( ) ;
return vm ( ) . heap ( ) . allocate < GeneratorResult > ( value , continuation_value , false ) . ptr ( ) ;
2024-05-18 17:25:43 +02:00
}
2023-06-15 12:36:57 +02:00
// 16.1.6 ScriptEvaluation ( scriptRecord ), https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation
2024-11-15 04:01:23 +13:00
ThrowCompletionOr < Value > Interpreter : : run ( Script & script_record , GC : : Ptr < Environment > lexical_environment_override )
2023-06-15 12:36:57 +02:00
{
auto & vm = this - > vm ( ) ;
// 1. Let globalEnv be scriptRecord.[[Realm]].[[GlobalEnv]].
auto & global_environment = script_record . realm ( ) . global_environment ( ) ;
2025-04-22 21:49:41 +02:00
// NOTE: Spec steps are rearranged in order to compute number of registers+constants+locals before construction of the execution 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 ( js_undefined ( ) ) ;
GC : : Ptr < Executable > executable ;
if ( result . type ( ) = = Completion : : Type : : Normal ) {
auto executable_result = JS : : Bytecode : : Generator : : generate_from_ast_node ( vm , 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 = vm . template throw_completion < JS : : InternalError > ( error_string . release_value ( ) ) ;
} else {
executable = executable_result . release_value ( ) ;
if ( g_dump_bytecode )
executable - > dump ( ) ;
}
}
u32 registers_and_constants_and_locals_count = 0 ;
if ( executable ) {
registers_and_constants_and_locals_count = executable - > number_of_registers + executable - > constants . size ( ) + executable - > local_variable_names . size ( ) ;
}
2023-06-15 12:36:57 +02:00
// 2. Let scriptContext be a new ECMAScript code execution context.
2025-04-23 23:44:24 +02:00
ExecutionContext * script_context = nullptr ;
ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK ( script_context , registers_and_constants_and_locals_count , 0 ) ;
2023-06-15 12:36:57 +02:00
// 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]].
2023-11-27 16:45:45 +01:00
script_context - > realm = & script_record . realm ( ) ;
2023-06-15 12:36:57 +02:00
// 5. Set the ScriptOrModule of scriptContext to scriptRecord.
2024-11-15 04:01:23 +13:00
script_context - > script_or_module = GC : : Ref < Script > ( script_record ) ;
2023-06-15 12:36:57 +02:00
// 6. Set the VariableEnvironment of scriptContext to globalEnv.
2023-11-27 16:45:45 +01:00
script_context - > variable_environment = & global_environment ;
2023-06-15 12:36:57 +02:00
// 7. Set the LexicalEnvironment of scriptContext to globalEnv.
2023-11-27 16:45:45 +01:00
script_context - > lexical_environment = & global_environment ;
2023-06-15 12:36:57 +02:00
2024-10-31 08:03:09 -04:00
// Non-standard: Override the lexical environment if requested.
if ( lexical_environment_override )
script_context - > lexical_environment = lexical_environment_override ;
2023-06-15 12:36:57 +02:00
// 8. Set the PrivateEnvironment of scriptContext to null.
// NOTE: This isn't in the spec, but we require it.
2023-11-27 16:45:45 +01:00
script_context - > is_strict_mode = script_record . parse_node ( ) . is_strict_mode ( ) ;
2023-06-15 12:36:57 +02:00
2025-01-01 22:51:52 +13:00
// 9. Suspend the currently running execution context.
2023-06-15 12:36:57 +02:00
// 10. Push scriptContext onto the execution context stack; scriptContext is now the running execution context.
2023-11-27 16:45:45 +01:00
TRY ( vm . push_execution_context ( * script_context , { } ) ) ;
2023-06-15 12:36:57 +02:00
// 13. If result.[[Type]] is normal, then
2025-04-22 21:49:41 +02:00
if ( executable ) {
2025-01-02 01:56:00 +13:00
// a. Set result to Completion(Evaluation of script).
2025-04-22 21:49:41 +02:00
auto result_or_error = run_executable ( * executable , { } , { } ) ;
if ( result_or_error . value . is_error ( ) )
result = result_or_error . value . release_error ( ) ;
else {
result = result_or_error . return_register_value . is_special_empty_value ( ) ? normal_completion ( js_undefined ( ) ) : result_or_error . return_register_value ;
2023-06-15 12:36:57 +02:00
}
2025-01-02 01:56:00 +13:00
// b. If result is a normal completion and result.[[Value]] is empty, then
2025-04-04 23:16:34 +02:00
if ( result . type ( ) = = Completion : : Type : : Normal & & result . value ( ) . is_special_empty_value ( ) ) {
2025-01-02 01:56:00 +13:00
// i. Set result to NormalCompletion(undefined).
result = normal_completion ( js_undefined ( ) ) ;
}
2023-06-15 12:36:57 +02:00
}
2025-01-02 01:56:00 +13:00
// 14. Suspend scriptContext and remove it from the execution context stack.
2023-06-15 12:36:57 +02:00
vm . pop_execution_context ( ) ;
2025-01-02 01:56:00 +13:00
// 15. Assert: The execution context stack is not empty.
2023-06-15 12:36:57 +02:00
VERIFY ( ! vm . execution_context_stack ( ) . is_empty ( ) ) ;
2025-01-02 01:56:00 +13:00
// FIXME: 16. Resume the context that is now on the top of the execution context stack as the running execution context.
2023-06-15 12:36:57 +02:00
vm . finish_execution_generation ( ) ;
2025-01-02 01:56:00 +13:00
// 17. Return ? result.
2023-06-15 12:36:57 +02:00
if ( result . is_abrupt ( ) ) {
VERIFY ( result . type ( ) = = Completion : : Type : : Throw ) ;
2025-04-04 23:16:34 +02:00
return result . release_error ( ) ;
2023-06-15 12:36:57 +02:00
}
2025-04-04 18:11:45 +02:00
return result . value ( ) ;
2023-06-15 12:36:57 +02:00
}
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 ( ) ;
}
2025-04-06 01:29:00 +02:00
NEVER_INLINE Interpreter : : HandleExceptionResponse Interpreter : : handle_exception ( size_t & program_counter , Value exception )
2021-06-03 10:46:30 +02:00
{
2024-05-06 06:44:08 +02:00
reg ( Register : : exception ( ) ) = exception ;
m_scheduled_jump = { } ;
auto handlers = current_executable ( ) . exception_handlers_for_offset ( program_counter ) ;
if ( ! handlers . has_value ( ) ) {
return HandleExceptionResponse : : ExitFromExecutable ;
}
auto & handler = handlers - > handler_offset ;
auto & finalizer = handlers - > finalizer_offset ;
2024-05-11 18:16:27 +02:00
VERIFY ( ! running_execution_context ( ) . unwind_contexts . is_empty ( ) ) ;
auto & unwind_context = running_execution_context ( ) . unwind_contexts . last ( ) ;
2024-05-06 06:44:08 +02:00
VERIFY ( unwind_context . executable = = m_current_executable ) ;
if ( handler . has_value ( ) ) {
program_counter = handler . value ( ) ;
return HandleExceptionResponse : : ContinueInThisExecutable ;
}
if ( finalizer . has_value ( ) ) {
program_counter = finalizer . value ( ) ;
return HandleExceptionResponse : : ContinueInThisExecutable ;
}
VERIFY_NOT_REACHED ( ) ;
}
2024-05-06 16:50:48 +02:00
// FIXME: GCC takes a *long* time to compile with flattening, and it will time out our CI. :|
# if defined(AK_COMPILER_CLANG)
# define FLATTEN_ON_CLANG FLATTEN
# else
# define FLATTEN_ON_CLANG
# endif
FLATTEN_ON_CLANG void Interpreter : : run_bytecode ( size_t entry_point )
2024-05-06 06:44:08 +02:00
{
2025-04-28 20:35:59 +02:00
if ( vm ( ) . did_reach_stack_space_limit ( ) ) [[unlikely]] {
2025-04-04 18:11:45 +02:00
reg ( Register : : exception ( ) ) = vm ( ) . throw_completion < InternalError > ( ErrorType : : CallStackSizeExceeded ) . value ( ) ;
2024-05-07 07:50:19 +02:00
return ;
}
2024-05-11 18:16:27 +02:00
auto & running_execution_context = this - > running_execution_context ( ) ;
2024-05-06 06:44:08 +02:00
auto & executable = current_executable ( ) ;
auto const * bytecode = executable . bytecode . data ( ) ;
2025-04-28 19:02:56 +02:00
size_t & program_counter = running_execution_context . program_counter ;
program_counter = entry_point ;
2024-05-06 06:44:08 +02:00
2024-05-06 16:44:45 +02:00
// Declare a lookup table for computed goto with each of the `handle_*` labels
// to avoid the overhead of a switch statement.
// This is a GCC extension, but it's also supported by Clang.
static void * const bytecode_dispatch_table [ ] = {
# define SET_UP_LABEL(name) &&handle_##name,
ENUMERATE_BYTECODE_OPS ( SET_UP_LABEL )
} ;
2024-05-09 15:13:31 +02:00
# undef SET_UP_LABEL
2024-05-06 16:44:45 +02:00
# define DISPATCH_NEXT(name) \
do { \
if constexpr ( Op : : name : : IsVariableLength ) \
2025-04-03 15:09:49 +02:00
program_counter + = instruction . length ( ) ; \
2024-05-06 16:44:45 +02:00
else \
program_counter + = sizeof ( Op : : name ) ; \
auto & next_instruction = * reinterpret_cast < Instruction const * > ( & bytecode [ program_counter ] ) ; \
goto * bytecode_dispatch_table [ static_cast < size_t > ( next_instruction . type ( ) ) ] ; \
} while ( 0 )
2021-06-09 06:49:58 +04:30
for ( ; ; ) {
2023-09-26 16:45:55 +02:00
start :
2024-05-06 06:44:08 +02:00
for ( ; ; ) {
2024-05-06 16:44:45 +02:00
goto * bytecode_dispatch_table [ static_cast < size_t > ( ( * reinterpret_cast < Instruction const * > ( & bytecode [ program_counter ] ) ) . type ( ) ) ] ;
2023-09-26 17:14:59 +02:00
2024-05-06 16:44:45 +02:00
handle_Mov : {
auto & instruction = * reinterpret_cast < Op : : Mov const * > ( & bytecode [ program_counter ] ) ;
2025-04-29 16:08:42 +02:00
set ( instruction . dst ( ) , get ( instruction . src ( ) ) ) ;
2024-05-06 16:44:45 +02:00
DISPATCH_NEXT ( Mov ) ;
}
handle_End : {
auto & instruction = * reinterpret_cast < Op : : End const * > ( & bytecode [ program_counter ] ) ;
2025-04-29 16:08:42 +02:00
accumulator ( ) = get ( instruction . value ( ) ) ;
2024-05-06 16:44:45 +02:00
return ;
}
handle_Jump : {
auto & instruction = * reinterpret_cast < Op : : Jump const * > ( & bytecode [ program_counter ] ) ;
program_counter = instruction . target ( ) . address ( ) ;
goto start ;
}
handle_JumpIf : {
auto & instruction = * reinterpret_cast < Op : : JumpIf const * > ( & bytecode [ program_counter ] ) ;
2025-04-29 16:08:42 +02:00
if ( get ( instruction . condition ( ) ) . to_boolean ( ) )
2024-05-06 16:44:45 +02:00
program_counter = instruction . true_target ( ) . address ( ) ;
else
program_counter = instruction . false_target ( ) . address ( ) ;
goto start ;
}
handle_JumpTrue : {
auto & instruction = * reinterpret_cast < Op : : JumpTrue const * > ( & bytecode [ program_counter ] ) ;
2025-04-29 16:08:42 +02:00
if ( get ( instruction . condition ( ) ) . to_boolean ( ) ) {
2024-05-06 16:44:45 +02:00
program_counter = instruction . target ( ) . address ( ) ;
2024-05-06 10:42:52 +02:00
goto start ;
2024-05-06 10:15:17 +02:00
}
2024-05-06 16:44:45 +02:00
DISPATCH_NEXT ( JumpTrue ) ;
}
handle_JumpFalse : {
auto & instruction = * reinterpret_cast < Op : : JumpFalse const * > ( & bytecode [ program_counter ] ) ;
2025-04-29 16:08:42 +02:00
if ( ! get ( instruction . condition ( ) ) . to_boolean ( ) ) {
2024-05-06 16:44:45 +02:00
program_counter = instruction . target ( ) . address ( ) ;
2024-05-06 10:42:52 +02:00
goto start ;
2024-05-06 10:15:17 +02:00
}
2024-05-06 16:44:45 +02:00
DISPATCH_NEXT ( JumpFalse ) ;
}
handle_JumpNullish : {
auto & instruction = * reinterpret_cast < Op : : JumpNullish const * > ( & bytecode [ program_counter ] ) ;
2025-04-29 16:08:42 +02:00
if ( get ( instruction . condition ( ) ) . is_nullish ( ) )
2024-05-06 16:44:45 +02:00
program_counter = instruction . true_target ( ) . address ( ) ;
else
program_counter = instruction . false_target ( ) . address ( ) ;
goto start ;
}
2024-05-13 09:23:53 +02:00
# define HANDLE_COMPARISON_OP(op_TitleCase, op_snake_case, numeric_operator) \
2024-05-10 11:22:27 +02:00
handle_Jump # # op_TitleCase : \
{ \
auto & instruction = * reinterpret_cast < Op : : Jump # # op_TitleCase const * > ( & bytecode [ program_counter ] ) ; \
2025-04-29 16:08:42 +02:00
auto lhs = get ( instruction . lhs ( ) ) ; \
auto rhs = get ( instruction . rhs ( ) ) ; \
2024-05-13 09:23:53 +02:00
if ( lhs . is_number ( ) & & rhs . is_number ( ) ) { \
bool result ; \
if ( lhs . is_int32 ( ) & & rhs . is_int32 ( ) ) { \
result = lhs . as_i32 ( ) numeric_operator rhs . as_i32 ( ) ; \
} else { \
result = lhs . as_double ( ) numeric_operator rhs . as_double ( ) ; \
} \
program_counter = result ? instruction . true_target ( ) . address ( ) : instruction . false_target ( ) . address ( ) ; \
goto start ; \
} \
2025-04-29 16:08:42 +02:00
auto result = op_snake_case ( vm ( ) , get ( instruction . lhs ( ) ) , get ( instruction . rhs ( ) ) ) ; \
2025-04-06 02:32:04 +02:00
if ( result . is_error ( ) ) [[unlikely]] { \
2024-05-10 11:22:27 +02:00
if ( handle_exception ( program_counter , result . error_value ( ) ) = = HandleExceptionResponse : : ExitFromExecutable ) \
return ; \
goto start ; \
} \
2025-05-08 15:19:35 +12:00
if ( result . value ( ) ) \
2024-05-10 11:22:27 +02:00
program_counter = instruction . true_target ( ) . address ( ) ; \
else \
program_counter = instruction . false_target ( ) . address ( ) ; \
goto start ; \
2024-05-09 15:13:31 +02:00
}
JS_ENUMERATE_COMPARISON_OPS ( HANDLE_COMPARISON_OP )
# undef HANDLE_COMPARISON_OP
2024-05-06 16:44:45 +02:00
handle_JumpUndefined : {
auto & instruction = * reinterpret_cast < Op : : JumpUndefined const * > ( & bytecode [ program_counter ] ) ;
2025-04-29 16:08:42 +02:00
if ( get ( instruction . condition ( ) ) . is_undefined ( ) )
2024-05-06 16:44:45 +02:00
program_counter = instruction . true_target ( ) . address ( ) ;
else
program_counter = instruction . false_target ( ) . address ( ) ;
goto start ;
}
handle_EnterUnwindContext : {
auto & instruction = * reinterpret_cast < Op : : EnterUnwindContext const * > ( & bytecode [ program_counter ] ) ;
enter_unwind_context ( ) ;
program_counter = instruction . entry_point ( ) . address ( ) ;
goto start ;
}
handle_ContinuePendingUnwind : {
auto & instruction = * reinterpret_cast < Op : : ContinuePendingUnwind const * > ( & bytecode [ program_counter ] ) ;
2025-04-04 23:16:34 +02:00
if ( auto exception = reg ( Register : : exception ( ) ) ; ! exception . is_special_empty_value ( ) ) {
2024-05-06 16:44:45 +02:00
if ( handle_exception ( program_counter , exception ) = = HandleExceptionResponse : : ExitFromExecutable )
return ;
2023-09-26 19:43:44 +02:00
goto start ;
2024-05-05 11:52:14 +02:00
}
2025-04-04 23:16:34 +02:00
if ( ! saved_return_value ( ) . is_special_empty_value ( ) ) {
2024-05-06 16:44:45 +02:00
do_return ( saved_return_value ( ) ) ;
2024-05-12 11:03:26 +02:00
if ( auto handlers = executable . exception_handlers_for_offset ( program_counter ) ; handlers . has_value ( ) ) {
if ( auto finalizer = handlers . value ( ) . finalizer_offset ; finalizer . has_value ( ) ) {
VERIFY ( ! running_execution_context . unwind_contexts . is_empty ( ) ) ;
auto & unwind_context = running_execution_context . unwind_contexts . last ( ) ;
VERIFY ( unwind_context . executable = = m_current_executable ) ;
reg ( Register : : saved_return_value ( ) ) = reg ( Register : : return_value ( ) ) ;
2025-04-04 23:16:34 +02:00
reg ( Register : : return_value ( ) ) = js_special_empty_value ( ) ;
2024-05-12 11:03:26 +02:00
program_counter = finalizer . value ( ) ;
// the unwind_context will be pop'ed when entering the finally block
goto start ;
}
}
return ;
2024-05-05 11:52:14 +02:00
}
2024-05-06 16:44:45 +02:00
auto const old_scheduled_jump = running_execution_context . previously_scheduled_jumps . take_last ( ) ;
if ( m_scheduled_jump . has_value ( ) ) {
program_counter = m_scheduled_jump . value ( ) ;
m_scheduled_jump = { } ;
} else {
program_counter = instruction . resume_target ( ) . address ( ) ;
// set the scheduled jump to the old value if we continue
// where we left it
m_scheduled_jump = old_scheduled_jump ;
2023-10-21 22:46:40 +02:00
}
2024-05-06 16:44:45 +02:00
goto start ;
}
handle_ScheduleJump : {
auto & instruction = * reinterpret_cast < Op : : ScheduleJump const * > ( & bytecode [ program_counter ] ) ;
m_scheduled_jump = instruction . target ( ) . address ( ) ;
auto finalizer = executable . exception_handlers_for_offset ( program_counter ) . value ( ) . finalizer_offset ;
VERIFY ( finalizer . has_value ( ) ) ;
program_counter = finalizer . value ( ) ;
goto start ;
}
2024-05-10 11:22:27 +02:00
# define HANDLE_INSTRUCTION(name) \
handle_ # # name : \
{ \
auto & instruction = * reinterpret_cast < Op : : name const * > ( & bytecode [ program_counter ] ) ; \
{ \
2025-04-29 16:08:42 +02:00
auto result = instruction . execute_impl ( * this ) ; \
2025-04-06 02:32:04 +02:00
if ( result . is_error ( ) ) [[unlikely]] { \
2024-05-10 11:22:27 +02:00
if ( handle_exception ( program_counter , result . error_value ( ) ) = = HandleExceptionResponse : : ExitFromExecutable ) \
return ; \
goto start ; \
} \
} \
DISPATCH_NEXT ( name ) ; \
2024-05-06 16:44:45 +02:00
}
2024-05-09 15:24:34 +02:00
# define HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(name) \
handle_ # # name : \
{ \
auto & instruction = * reinterpret_cast < Op : : name const * > ( & bytecode [ program_counter ] ) ; \
2025-04-29 16:08:42 +02:00
instruction . execute_impl ( * this ) ; \
2024-05-09 15:24:34 +02:00
DISPATCH_NEXT ( name ) ; \
}
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( Add ) ;
2024-05-11 22:54:41 +00:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( AddPrivateName ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( ArrayAppend ) ;
HANDLE_INSTRUCTION ( AsyncIteratorClose ) ;
HANDLE_INSTRUCTION ( BitwiseAnd ) ;
HANDLE_INSTRUCTION ( BitwiseNot ) ;
HANDLE_INSTRUCTION ( BitwiseOr ) ;
HANDLE_INSTRUCTION ( BitwiseXor ) ;
2024-05-09 15:24:34 +02:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( BlockDeclarationInstantiation ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( Call ) ;
2024-10-31 22:47:30 +01:00
HANDLE_INSTRUCTION ( CallBuiltin ) ;
HANDLE_INSTRUCTION ( CallConstruct ) ;
2025-08-30 11:00:54 +02:00
HANDLE_INSTRUCTION ( CallConstructWithArgumentArray ) ;
2024-10-31 22:47:30 +01:00
HANDLE_INSTRUCTION ( CallDirectEval ) ;
2025-08-30 11:00:54 +02:00
HANDLE_INSTRUCTION ( CallDirectEvalWithArgumentArray ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( CallWithArgumentArray ) ;
2024-05-09 15:24:34 +02:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( Catch ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( ConcatString ) ;
HANDLE_INSTRUCTION ( CopyObjectExcludingProperties ) ;
2024-05-09 15:24:34 +02:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( CreateLexicalEnvironment ) ;
2024-05-05 22:06:55 +02:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( CreateVariableEnvironment ) ;
2024-05-11 22:54:41 +00:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( CreatePrivateEnvironment ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( CreateVariable ) ;
2025-04-04 13:48:59 +02:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( CreateRestParams ) ;
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( CreateArguments ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( Decrement ) ;
HANDLE_INSTRUCTION ( DeleteById ) ;
HANDLE_INSTRUCTION ( DeleteByIdWithThis ) ;
HANDLE_INSTRUCTION ( DeleteByValue ) ;
HANDLE_INSTRUCTION ( DeleteByValueWithThis ) ;
HANDLE_INSTRUCTION ( DeleteVariable ) ;
HANDLE_INSTRUCTION ( Div ) ;
2024-05-09 15:24:34 +02:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( Dump ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( EnterObjectEnvironment ) ;
HANDLE_INSTRUCTION ( Exp ) ;
HANDLE_INSTRUCTION ( GetById ) ;
HANDLE_INSTRUCTION ( GetByIdWithThis ) ;
HANDLE_INSTRUCTION ( GetByValue ) ;
HANDLE_INSTRUCTION ( GetByValueWithThis ) ;
HANDLE_INSTRUCTION ( GetCalleeAndThisFromEnvironment ) ;
2025-03-31 09:32:39 +01:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( GetCompletionFields ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( GetGlobal ) ;
2024-05-09 15:24:34 +02:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( GetImportMeta ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( GetIterator ) ;
2024-05-20 11:53:28 +02:00
HANDLE_INSTRUCTION ( GetLength ) ;
HANDLE_INSTRUCTION ( GetLengthWithThis ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( GetMethod ) ;
2024-05-09 15:24:34 +02:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( GetNewTarget ) ;
2025-04-04 13:48:59 +02:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( GetNextMethodFromIteratorRecord ) ;
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( GetObjectFromIteratorRecord ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( GetObjectPropertyIterator ) ;
HANDLE_INSTRUCTION ( GetPrivateById ) ;
2024-05-14 11:32:04 +02:00
HANDLE_INSTRUCTION ( GetBinding ) ;
2025-05-04 01:41:49 +02:00
HANDLE_INSTRUCTION ( GetInitializedBinding ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( GreaterThan ) ;
HANDLE_INSTRUCTION ( GreaterThanEquals ) ;
HANDLE_INSTRUCTION ( HasPrivateId ) ;
HANDLE_INSTRUCTION ( ImportCall ) ;
HANDLE_INSTRUCTION ( In ) ;
HANDLE_INSTRUCTION ( Increment ) ;
2024-05-14 11:30:30 +02:00
HANDLE_INSTRUCTION ( InitializeLexicalBinding ) ;
HANDLE_INSTRUCTION ( InitializeVariableBinding ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( InstanceOf ) ;
HANDLE_INSTRUCTION ( IteratorClose ) ;
HANDLE_INSTRUCTION ( IteratorNext ) ;
2025-05-01 16:05:24 +03:00
HANDLE_INSTRUCTION ( IteratorNextUnpack ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( IteratorToArray ) ;
2024-05-09 15:24:34 +02:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( LeaveFinally ) ;
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( LeaveLexicalEnvironment ) ;
2024-05-11 22:54:41 +00:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( LeavePrivateEnvironment ) ;
2024-05-10 17:32:19 +02:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( LeaveUnwindContext ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( LeftShift ) ;
HANDLE_INSTRUCTION ( LessThan ) ;
HANDLE_INSTRUCTION ( LessThanEquals ) ;
HANDLE_INSTRUCTION ( LooselyEquals ) ;
HANDLE_INSTRUCTION ( LooselyInequals ) ;
HANDLE_INSTRUCTION ( Mod ) ;
HANDLE_INSTRUCTION ( Mul ) ;
2024-05-09 15:24:34 +02:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( NewArray ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( NewClass ) ;
2024-05-09 15:24:34 +02:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( NewFunction ) ;
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( NewObject ) ;
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( NewPrimitiveArray ) ;
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( NewRegExp ) ;
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( NewTypeError ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( Not ) ;
2024-05-18 17:25:43 +02:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( PrepareYield ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( PostfixDecrement ) ;
HANDLE_INSTRUCTION ( PostfixIncrement ) ;
HANDLE_INSTRUCTION ( PutById ) ;
2025-09-13 16:20:41 +02:00
HANDLE_INSTRUCTION ( PutByNumericId ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( PutByIdWithThis ) ;
2024-11-01 22:00:32 +01:00
HANDLE_INSTRUCTION ( PutBySpread ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( PutByValue ) ;
HANDLE_INSTRUCTION ( PutByValueWithThis ) ;
HANDLE_INSTRUCTION ( PutPrivateById ) ;
HANDLE_INSTRUCTION ( ResolveSuperBase ) ;
HANDLE_INSTRUCTION ( ResolveThisBinding ) ;
2024-05-10 17:32:19 +02:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( RestoreScheduledJump ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( RightShift ) ;
2025-03-31 09:32:39 +01:00
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK ( SetCompletionType ) ;
2025-05-01 23:58:38 +02:00
HANDLE_INSTRUCTION ( SetGlobal ) ;
2024-05-14 11:30:30 +02:00
HANDLE_INSTRUCTION ( SetLexicalBinding ) ;
HANDLE_INSTRUCTION ( SetVariableBinding ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( StrictlyEquals ) ;
HANDLE_INSTRUCTION ( StrictlyInequals ) ;
HANDLE_INSTRUCTION ( Sub ) ;
HANDLE_INSTRUCTION ( SuperCallWithArgumentArray ) ;
HANDLE_INSTRUCTION ( Throw ) ;
HANDLE_INSTRUCTION ( ThrowIfNotObject ) ;
HANDLE_INSTRUCTION ( ThrowIfNullish ) ;
HANDLE_INSTRUCTION ( ThrowIfTDZ ) ;
HANDLE_INSTRUCTION ( Typeof ) ;
2024-06-14 09:37:26 +02:00
HANDLE_INSTRUCTION ( TypeofBinding ) ;
2024-05-06 16:44:45 +02:00
HANDLE_INSTRUCTION ( UnaryMinus ) ;
HANDLE_INSTRUCTION ( UnaryPlus ) ;
HANDLE_INSTRUCTION ( UnsignedRightShift ) ;
handle_Await : {
auto & instruction = * reinterpret_cast < Op : : Await const * > ( & bytecode [ program_counter ] ) ;
2025-04-29 16:08:42 +02:00
instruction . execute_impl ( * this ) ;
2024-05-12 11:03:26 +02:00
return ;
2024-05-06 16:44:45 +02:00
}
2024-05-06 10:42:52 +02:00
2024-05-06 16:44:45 +02:00
handle_Return : {
auto & instruction = * reinterpret_cast < Op : : Return const * > ( & bytecode [ program_counter ] ) ;
2025-04-29 16:08:42 +02:00
instruction . execute_impl ( * this ) ;
2024-05-12 11:03:26 +02:00
return ;
2024-05-06 16:44:45 +02:00
}
2023-09-26 19:43:44 +02:00
2024-05-06 16:44:45 +02:00
handle_Yield : {
auto & instruction = * reinterpret_cast < Op : : Yield const * > ( & bytecode [ program_counter ] ) ;
2025-04-29 16:08:42 +02:00
instruction . execute_impl ( * this ) ;
2024-05-10 07:56:39 +02: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
2024-05-12 11:03:26 +02:00
return ;
2024-05-06 16:44:45 +02:00
}
2024-05-06 06:44:08 +02:00
}
2021-06-04 12:07:38 +02:00
}
2023-09-26 16:41:42 +02:00
}
2025-10-07 16:47:41 +02:00
Utf16FlyString const & Interpreter : : get_identifier ( IdentifierTableIndex index ) const
{
return m_identifier_table . data ( ) [ index . value ] ;
}
2024-05-06 06:44:08 +02:00
Interpreter : : ResultAndReturnRegister Interpreter : : run_executable ( Executable & executable , Optional < size_t > entry_point , Value initial_accumulator_value )
2023-09-26 16:41:42 +02:00
{
dbgln_if ( JS_BYTECODE_DEBUG , " Bytecode::Interpreter will run unit {:p} " , & executable ) ;
2024-11-15 04:01:23 +13:00
TemporaryChange restore_executable { m_current_executable , GC : : Ptr { executable } } ;
2024-05-06 06:44:08 +02:00
TemporaryChange restore_saved_jump { m_scheduled_jump , Optional < size_t > { } } ;
2024-11-15 04:01:23 +13:00
TemporaryChange restore_realm { m_realm , GC : : Ptr { vm ( ) . current_realm ( ) } } ;
TemporaryChange restore_global_object { m_global_object , GC : : Ptr { m_realm - > global_object ( ) } } ;
TemporaryChange restore_global_declarative_environment { m_global_declarative_environment , GC : : Ptr { m_realm - > global_environment ( ) . declarative_record ( ) } } ;
2025-10-07 16:47:41 +02:00
TemporaryChange restore_identifier_table { m_identifier_table , executable . identifier_table - > identifiers ( ) } ;
2023-09-26 16:41:42 +02:00
2024-05-01 19:33:49 +02:00
auto & running_execution_context = vm ( ) . running_execution_context ( ) ;
2024-06-13 21:19:06 +02:00
u32 registers_and_constants_and_locals_count = executable . number_of_registers + executable . constants . size ( ) + executable . local_variable_names . size ( ) ;
2025-04-23 19:44:54 +02:00
VERIFY ( registers_and_constants_and_locals_count < = running_execution_context . registers_and_constants_and_locals_and_arguments_span ( ) . size ( ) ) ;
2023-09-26 16:41:42 +02:00
2024-05-11 18:16:27 +02:00
TemporaryChange restore_running_execution_context { m_running_execution_context , & running_execution_context } ;
2025-04-24 22:10:41 +02:00
TemporaryChange restore_registers_and_constants_and_locals { m_registers_and_constants_and_locals_arguments , running_execution_context . registers_and_constants_and_locals_and_arguments_span ( ) } ;
2024-05-06 06:44:08 +02:00
reg ( Register : : accumulator ( ) ) = initial_accumulator_value ;
2025-04-04 23:16:34 +02:00
reg ( Register : : return_value ( ) ) = js_special_empty_value ( ) ;
2024-06-20 09:51:06 +02:00
// NOTE: We only copy the `this` value from ExecutionContext if it's not already set.
// If we are re-entering an async/generator context, the `this` value
// may have already been cached by a ResolveThisBinding instruction,
// and subsequent instructions expect this value to be set.
2025-04-04 23:16:34 +02:00
if ( reg ( Register : : this_value ( ) ) . is_special_empty_value ( ) )
reg ( Register : : this_value ( ) ) = running_execution_context . this_value . value_or ( js_special_empty_value ( ) ) ;
2024-05-01 19:33:49 +02:00
running_execution_context . executable = & executable ;
2023-11-01 00:39:28 +01:00
2025-04-23 19:44:54 +02:00
auto * registers_and_constants_and_locals_and_arguments = running_execution_context . registers_and_constants_and_locals_and_arguments ( ) ;
2024-05-12 18:49:03 +02:00
for ( size_t i = 0 ; i < executable . constants . size ( ) ; + + i ) {
2025-04-23 19:44:54 +02:00
registers_and_constants_and_locals_and_arguments [ executable . number_of_registers + i ] = executable . constants [ i ] ;
2024-05-12 18:49:03 +02:00
}
2024-05-06 06:44:08 +02:00
run_bytecode ( entry_point . value_or ( 0 ) ) ;
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 ) {
2024-05-12 18:49:03 +02:00
for ( size_t i = 0 ; i < executable . number_of_registers ; + + i ) {
2023-02-12 21:54:02 -05:00
String value_string ;
2025-04-23 00:57:07 +02:00
if ( registers_and_constants_and_locals_and_arguments [ i ] . is_special_empty_value ( ) )
2023-08-07 11:12:38 +02:00
value_string = " (empty) " _string ;
2021-06-07 15:17:37 +02:00
else
2025-04-23 00:57:07 +02:00
value_string = registers_and_constants_and_locals_and_arguments [ 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 ( ) ;
2025-04-04 23:16:34 +02:00
if ( ! reg ( Register : : return_value ( ) ) . is_special_empty_value ( ) )
2023-09-26 15:32:46 +02:00
return_value = reg ( Register : : return_value ( ) ) ;
2023-09-26 11:57:42 +02:00
auto exception = reg ( Register : : exception ( ) ) ;
2023-07-21 17:30:07 +02:00
2021-11-11 00:44:56 +03:30
vm ( ) . run_queued_promise_jobs ( ) ;
2021-06-12 17:32:54 +03:00
vm ( ) . finish_execution_generation ( ) ;
2025-04-04 23:16:34 +02:00
if ( ! exception . is_special_empty_value ( ) )
2025-04-23 19:44:54 +02:00
return { throw_completion ( exception ) , registers_and_constants_and_locals_and_arguments [ 0 ] } ;
return { return_value , registers_and_constants_and_locals_and_arguments [ 0 ] } ;
2021-06-03 10:46:30 +02:00
}
2023-10-20 00:33:51 +02:00
void Interpreter : : enter_unwind_context ( )
2021-06-10 15:04:38 +02:00
{
2024-05-11 18:16:27 +02:00
running_execution_context ( ) . unwind_contexts . empend (
2023-05-13 18:53:14 +02:00
m_current_executable ,
2024-05-11 18:16:27 +02:00
running_execution_context ( ) . lexical_environment ) ;
running_execution_context ( ) . previously_scheduled_jumps . append ( m_scheduled_jump ) ;
2024-05-06 06:44:08 +02:00
m_scheduled_jump = { } ;
2021-06-10 15:04:38 +02:00
}
void Interpreter : : leave_unwind_context ( )
{
2024-05-11 18:16:27 +02:00
running_execution_context ( ) . unwind_contexts . take_last ( ) ;
2021-06-10 15:04:38 +02:00
}
2025-04-29 16:08:42 +02:00
void Interpreter : : catch_exception ( Operand dst )
{
set ( dst , reg ( Register : : exception ( ) ) ) ;
reg ( Register : : exception ( ) ) = js_special_empty_value ( ) ;
auto & context = running_execution_context ( ) . unwind_contexts . last ( ) ;
VERIFY ( ! context . handler_called ) ;
VERIFY ( context . executable = = & current_executable ( ) ) ;
context . handler_called = true ;
running_execution_context ( ) . lexical_environment = context . lexical_environment ;
}
2024-04-11 11:07:35 +02:00
void Interpreter : : restore_scheduled_jump ( )
{
2024-05-11 18:16:27 +02:00
m_scheduled_jump = running_execution_context ( ) . previously_scheduled_jumps . take_last ( ) ;
2024-04-11 11:07:35 +02:00
}
2024-04-11 11:58:18 +02:00
void Interpreter : : leave_finally ( )
{
2025-04-04 23:16:34 +02:00
reg ( Register : : exception ( ) ) = js_special_empty_value ( ) ;
2024-05-11 18:16:27 +02:00
m_scheduled_jump = running_execution_context ( ) . previously_scheduled_jumps . take_last ( ) ;
2024-04-11 11:58:18 +02:00
}
2023-11-12 00:12:21 +01:00
void Interpreter : : enter_object_environment ( Object & object )
{
2024-05-11 18:16:27 +02:00
auto & old_environment = running_execution_context ( ) . lexical_environment ;
running_execution_context ( ) . saved_lexical_environments . append ( old_environment ) ;
running_execution_context ( ) . lexical_environment = new_object_environment ( object , true , old_environment ) ;
2023-11-12 00:12:21 +01:00
}
2025-08-02 19:27:29 -04:00
ThrowCompletionOr < GC : : Ref < Bytecode : : Executable > > compile ( VM & vm , ASTNode const & node , FunctionKind kind , Utf16FlyString const & name )
2023-06-22 15:59:18 +02:00
{
2024-05-05 22:06:55 +02:00
auto executable_result = Bytecode : : Generator : : generate_from_ast_node ( vm , 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 ;
if ( Bytecode : : g_dump_bytecode )
bytecode_executable - > dump ( ) ;
return bytecode_executable ;
}
2024-11-15 04:01:23 +13:00
ThrowCompletionOr < GC : : Ref < Bytecode : : Executable > > compile ( VM & vm , ECMAScriptFunctionObject const & function )
2024-05-05 22:06:55 +02:00
{
auto const & name = function . name ( ) ;
auto executable_result = Bytecode : : Generator : : generate_from_function ( vm , function ) ;
2023-06-22 15:59:18 +02:00
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 ;
}
2024-07-09 10:10:14 +02:00
// NOTE: This function assumes that the index is valid within the TypedArray,
// and that the TypedArray is not detached.
template < typename T >
inline Value fast_typed_array_get_element ( TypedArrayBase & typed_array , u32 index )
{
Checked < u32 > offset_into_array_buffer = index ;
offset_into_array_buffer * = sizeof ( T ) ;
offset_into_array_buffer + = typed_array . byte_offset ( ) ;
if ( offset_into_array_buffer . has_overflow ( ) ) [[unlikely]] {
return js_undefined ( ) ;
}
auto const & array_buffer = * typed_array . viewed_array_buffer ( ) ;
auto const * slot = reinterpret_cast < T const * > ( array_buffer . buffer ( ) . offset_pointer ( offset_into_array_buffer . value ( ) ) ) ;
return Value { * slot } ;
2021-06-03 10:46:30 +02:00
}
2023-09-27 10:10:00 +02:00
2024-07-09 10:10:14 +02:00
// NOTE: This function assumes that the index is valid within the TypedArray,
// and that the TypedArray is not detached.
template < typename T >
inline void fast_typed_array_set_element ( TypedArrayBase & typed_array , u32 index , T value )
{
Checked < u32 > offset_into_array_buffer = index ;
offset_into_array_buffer * = sizeof ( T ) ;
offset_into_array_buffer + = typed_array . byte_offset ( ) ;
if ( offset_into_array_buffer . has_overflow ( ) ) [[unlikely]] {
return ;
}
auto & array_buffer = * typed_array . viewed_array_buffer ( ) ;
auto * slot = reinterpret_cast < T * > ( array_buffer . buffer ( ) . offset_pointer ( offset_into_array_buffer . value ( ) ) ) ;
* slot = value ;
}
2024-07-14 18:29:33 +02:00
static Completion throw_null_or_undefined_property_get ( VM & vm , Value base_value , Optional < IdentifierTableIndex > base_identifier , IdentifierTableIndex property_identifier , Executable const & executable )
2024-07-09 11:37:06 +02:00
{
VERIFY ( base_value . is_nullish ( ) ) ;
if ( base_identifier . has_value ( ) )
return vm . throw_completion < TypeError > ( ErrorType : : ToObjectNullOrUndefinedWithPropertyAndName , executable . get_identifier ( property_identifier ) , base_value , executable . get_identifier ( base_identifier . value ( ) ) ) ;
return vm . throw_completion < TypeError > ( ErrorType : : ToObjectNullOrUndefinedWithProperty , executable . get_identifier ( property_identifier ) , base_value ) ;
}
2024-07-14 18:29:33 +02:00
static Completion throw_null_or_undefined_property_get ( VM & vm , Value base_value , Optional < IdentifierTableIndex > base_identifier , Value property , Executable const & executable )
2024-07-09 11:37:06 +02:00
{
VERIFY ( base_value . is_nullish ( ) ) ;
if ( base_identifier . has_value ( ) )
return vm . throw_completion < TypeError > ( ErrorType : : ToObjectNullOrUndefinedWithPropertyAndName , property , base_value , executable . get_identifier ( base_identifier . value ( ) ) ) ;
return vm . throw_completion < TypeError > ( ErrorType : : ToObjectNullOrUndefinedWithProperty , property , base_value ) ;
}
2024-07-09 10:10:14 +02:00
template < typename BaseType , typename PropertyType >
ALWAYS_INLINE Completion throw_null_or_undefined_property_access ( VM & vm , Value base_value , BaseType const & base_identifier , PropertyType const & property_identifier )
{
VERIFY ( base_value . is_nullish ( ) ) ;
bool has_base_identifier = true ;
bool has_property_identifier = true ;
if constexpr ( requires { base_identifier . has_value ( ) ; } )
has_base_identifier = base_identifier . has_value ( ) ;
if constexpr ( requires { property_identifier . has_value ( ) ; } )
has_property_identifier = property_identifier . has_value ( ) ;
if ( has_base_identifier & & has_property_identifier )
return vm . throw_completion < TypeError > ( ErrorType : : ToObjectNullOrUndefinedWithPropertyAndName , property_identifier , base_value , base_identifier ) ;
if ( has_property_identifier )
return vm . throw_completion < TypeError > ( ErrorType : : ToObjectNullOrUndefinedWithProperty , property_identifier , base_value ) ;
if ( has_base_identifier )
return vm . throw_completion < TypeError > ( ErrorType : : ToObjectNullOrUndefinedWithName , base_identifier , base_value ) ;
return vm . throw_completion < TypeError > ( ErrorType : : ToObjectNullOrUndefined ) ;
}
2024-11-15 04:01:23 +13:00
ALWAYS_INLINE GC : : Ptr < Object > base_object_for_get_impl ( VM & vm , Value base_value )
2024-07-09 10:10:14 +02:00
{
if ( base_value . is_object ( ) ) [[likely]]
return base_value . as_object ( ) ;
// OPTIMIZATION: For various primitives we can avoid actually creating a new object for them.
auto & realm = * vm . current_realm ( ) ;
if ( base_value . is_string ( ) )
return realm . intrinsics ( ) . string_prototype ( ) ;
if ( base_value . is_number ( ) )
return realm . intrinsics ( ) . number_prototype ( ) ;
if ( base_value . is_boolean ( ) )
return realm . intrinsics ( ) . boolean_prototype ( ) ;
if ( base_value . is_bigint ( ) )
return realm . intrinsics ( ) . bigint_prototype ( ) ;
if ( base_value . is_symbol ( ) )
return realm . intrinsics ( ) . symbol_prototype ( ) ;
2024-07-09 11:37:06 +02:00
return nullptr ;
}
2024-11-15 04:01:23 +13:00
ALWAYS_INLINE ThrowCompletionOr < GC : : Ref < Object > > base_object_for_get ( VM & vm , Value base_value , Optional < IdentifierTableIndex > base_identifier , IdentifierTableIndex property_identifier , Executable const & executable )
2024-07-09 11:37:06 +02:00
{
if ( auto base_object = base_object_for_get_impl ( vm , base_value ) )
2024-11-15 04:01:23 +13:00
return GC : : Ref { * base_object } ;
2024-07-09 11:37:06 +02:00
2024-07-09 10:10:14 +02:00
// NOTE: At this point this is guaranteed to throw (null or undefined).
2024-07-09 11:37:06 +02:00
return throw_null_or_undefined_property_get ( vm , base_value , base_identifier , property_identifier , executable ) ;
}
2024-11-15 04:01:23 +13:00
ALWAYS_INLINE ThrowCompletionOr < GC : : Ref < Object > > base_object_for_get ( VM & vm , Value base_value , Optional < IdentifierTableIndex > base_identifier , Value property , Executable const & executable )
2024-07-09 11:37:06 +02:00
{
if ( auto base_object = base_object_for_get_impl ( vm , base_value ) )
2024-11-15 04:01:23 +13:00
return GC : : Ref { * base_object } ;
2024-07-09 11:37:06 +02:00
// NOTE: At this point this is guaranteed to throw (null or undefined).
return throw_null_or_undefined_property_get ( vm , base_value , base_identifier , property , executable ) ;
2024-07-09 10:10:14 +02:00
}
enum class GetByIdMode {
Normal ,
Length ,
} ;
template < GetByIdMode mode = GetByIdMode : : Normal >
2024-07-09 11:37:06 +02:00
inline ThrowCompletionOr < Value > get_by_id ( VM & vm , Optional < IdentifierTableIndex > base_identifier , IdentifierTableIndex property , Value base_value , Value this_value , PropertyLookupCache & cache , Executable const & executable )
2024-07-09 10:10:14 +02:00
{
if constexpr ( mode = = GetByIdMode : : Length ) {
if ( base_value . is_string ( ) ) {
2025-05-04 00:06:34 +12:00
return Value ( base_value . as_string ( ) . length_in_utf16_code_units ( ) ) ;
2024-07-09 10:10:14 +02:00
}
}
2024-07-09 11:37:06 +02:00
auto base_obj = TRY ( base_object_for_get ( vm , base_value , base_identifier , property , executable ) ) ;
2024-07-09 10:10:14 +02:00
if constexpr ( mode = = GetByIdMode : : Length ) {
// OPTIMIZATION: Fast path for the magical "length" property on Array objects.
if ( base_obj - > has_magical_length_property ( ) ) {
return Value { base_obj - > indexed_properties ( ) . array_like_size ( ) } ;
}
}
auto & shape = base_obj - > shape ( ) ;
2025-09-18 02:28:52 +02:00
GC : : Ptr < PrototypeChainValidity > prototype_chain_validity ;
if ( shape . prototype ( ) )
prototype_chain_validity = shape . prototype ( ) - > shape ( ) . prototype_chain_validity ( ) ;
2025-05-06 13:16:44 +02:00
for ( auto & cache_entry : cache . entries ) {
if ( cache_entry . prototype ) {
// OPTIMIZATION: If the prototype chain hasn't been mutated in a way that would invalidate the cache, we can use it.
bool can_use_cache = [ & ] ( ) - > bool {
if ( & shape ! = cache_entry . shape )
return false ;
if ( ! cache_entry . prototype_chain_validity )
return false ;
if ( ! cache_entry . prototype_chain_validity - > is_valid ( ) )
return false ;
return true ;
} ( ) ;
if ( can_use_cache ) {
auto value = cache_entry . prototype - > get_direct ( cache_entry . property_offset . value ( ) ) ;
if ( value . is_accessor ( ) )
return TRY ( call ( vm , value . as_accessor ( ) . getter ( ) , this_value ) ) ;
return value ;
}
} else if ( & shape = = cache_entry . shape ) {
// OPTIMIZATION: If the shape of the object hasn't changed, we can use the cached property offset.
auto value = base_obj - > get_direct ( cache_entry . property_offset . value ( ) ) ;
2024-10-17 20:29:07 +02:00
if ( value . is_accessor ( ) )
return TRY ( call ( vm , value . as_accessor ( ) . getter ( ) , this_value ) ) ;
return value ;
}
2024-07-09 10:10:14 +02:00
}
2025-09-15 17:23:39 +02:00
CacheableGetPropertyMetadata cacheable_metadata ;
2024-07-09 11:37:06 +02:00
auto value = TRY ( base_obj - > internal_get ( executable . get_identifier ( property ) , this_value , & cacheable_metadata ) ) ;
2024-07-09 10:10:14 +02:00
2025-05-20 23:42:17 +03:00
// If internal_get() caused object's shape change, we can no longer be sure
// that collected metadata is valid, e.g. if getter in prototype chain added
// property with the same name into the object itself.
if ( & shape = = & base_obj - > shape ( ) ) {
auto get_cache_slot = [ & ] - > PropertyLookupCache : : Entry & {
for ( size_t i = cache . entries . size ( ) - 1 ; i > = 1 ; - - i ) {
cache . entries [ i ] = cache . entries [ i - 1 ] ;
}
cache . entries [ 0 ] = { } ;
return cache . entries [ 0 ] ;
} ;
2025-09-15 17:23:39 +02:00
if ( cacheable_metadata . type = = CacheableGetPropertyMetadata : : Type : : GetOwnProperty ) {
2025-05-20 23:42:17 +03:00
auto & entry = get_cache_slot ( ) ;
entry . shape = shape ;
entry . property_offset = cacheable_metadata . property_offset . value ( ) ;
2025-09-15 17:23:39 +02:00
} else if ( cacheable_metadata . type = = CacheableGetPropertyMetadata : : Type : : GetPropertyInPrototypeChain ) {
2025-05-20 23:42:17 +03:00
auto & entry = get_cache_slot ( ) ;
entry . shape = & base_obj - > shape ( ) ;
entry . property_offset = cacheable_metadata . property_offset . value ( ) ;
entry . prototype = * cacheable_metadata . prototype ;
2025-09-18 02:28:52 +02:00
entry . prototype_chain_validity = * prototype_chain_validity ;
2025-05-06 13:16:44 +02:00
}
2024-07-09 10:10:14 +02:00
}
return value ;
}
2024-07-09 11:37:06 +02:00
inline ThrowCompletionOr < Value > get_by_value ( VM & vm , Optional < IdentifierTableIndex > base_identifier , Value base_value , Value property_key_value , Executable const & executable )
2024-07-09 10:10:14 +02:00
{
// OPTIMIZATION: Fast path for simple Int32 indexes in array-like objects.
if ( base_value . is_object ( ) & & property_key_value . is_int32 ( ) & & property_key_value . as_i32 ( ) > = 0 ) {
auto & object = base_value . as_object ( ) ;
auto index = static_cast < u32 > ( property_key_value . as_i32 ( ) ) ;
auto const * object_storage = object . indexed_properties ( ) . storage ( ) ;
// For "non-typed arrays":
if ( ! object . may_interfere_with_indexed_property_access ( )
& & object_storage ) {
auto maybe_value = [ & ] {
if ( object_storage - > is_simple_storage ( ) )
return static_cast < SimpleIndexedPropertyStorage const * > ( object_storage ) - > inline_get ( index ) ;
else
return static_cast < GenericIndexedPropertyStorage const * > ( object_storage ) - > get ( index ) ;
} ( ) ;
if ( maybe_value . has_value ( ) ) {
auto value = maybe_value - > value ;
if ( ! value . is_accessor ( ) )
return value ;
}
}
// For typed arrays:
if ( object . is_typed_array ( ) ) {
auto & typed_array = static_cast < TypedArrayBase & > ( object ) ;
auto canonical_index = CanonicalIndex { CanonicalIndex : : Type : : Index , index } ;
if ( is_valid_integer_index ( typed_array , canonical_index ) ) {
switch ( typed_array . kind ( ) ) {
case TypedArrayBase : : Kind : : Uint8Array :
return fast_typed_array_get_element < u8 > ( typed_array , index ) ;
case TypedArrayBase : : Kind : : Uint16Array :
return fast_typed_array_get_element < u16 > ( typed_array , index ) ;
case TypedArrayBase : : Kind : : Uint32Array :
return fast_typed_array_get_element < u32 > ( typed_array , index ) ;
case TypedArrayBase : : Kind : : Int8Array :
return fast_typed_array_get_element < i8 > ( typed_array , index ) ;
case TypedArrayBase : : Kind : : Int16Array :
return fast_typed_array_get_element < i16 > ( typed_array , index ) ;
case TypedArrayBase : : Kind : : Int32Array :
return fast_typed_array_get_element < i32 > ( typed_array , index ) ;
case TypedArrayBase : : Kind : : Uint8ClampedArray :
return fast_typed_array_get_element < u8 > ( typed_array , index ) ;
2024-11-18 01:42:36 +01:00
case TypedArrayBase : : Kind : : Float16Array :
return fast_typed_array_get_element < f16 > ( typed_array , index ) ;
case TypedArrayBase : : Kind : : Float32Array :
return fast_typed_array_get_element < float > ( typed_array , index ) ;
case TypedArrayBase : : Kind : : Float64Array :
return fast_typed_array_get_element < double > ( typed_array , index ) ;
2024-07-09 10:10:14 +02:00
default :
// FIXME: Support more TypedArray kinds.
break ;
}
}
switch ( typed_array . kind ( ) ) {
# define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
case TypedArrayBase : : Kind : : ClassName : \
return typed_array_get_element < Type > ( typed_array , canonical_index ) ;
JS_ENUMERATE_TYPED_ARRAYS
# undef __JS_ENUMERATE
}
}
}
2024-07-09 11:37:06 +02:00
auto object = TRY ( base_object_for_get ( vm , base_value , base_identifier , property_key_value , executable ) ) ;
2024-07-09 10:10:14 +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 ( ) )
return * string_value ;
}
return TRY ( object - > internal_get ( property_key , base_value ) ) ;
}
inline ThrowCompletionOr < Value > get_global ( Interpreter & interpreter , IdentifierTableIndex identifier_index , GlobalVariableCache & cache )
{
auto & vm = interpreter . vm ( ) ;
auto & binding_object = interpreter . global_object ( ) ;
auto & declarative_record = interpreter . global_declarative_environment ( ) ;
auto & shape = binding_object . shape ( ) ;
if ( cache . environment_serial_number = = declarative_record . environment_serial_number ( ) ) {
// OPTIMIZATION: For global var bindings, if the shape of the global object hasn't changed,
// we can use the cached property offset.
2025-05-06 13:16:44 +02:00
if ( & shape = = cache . entries [ 0 ] . shape ) {
auto value = binding_object . get_direct ( cache . entries [ 0 ] . property_offset . value ( ) ) ;
2024-10-17 20:29:07 +02:00
if ( value . is_accessor ( ) )
return TRY ( call ( vm , value . as_accessor ( ) . getter ( ) , js_undefined ( ) ) ) ;
2024-11-12 18:38:35 +01:00
return value ;
2024-07-09 10:10:14 +02:00
}
// OPTIMIZATION: For global lexical bindings, if the global declarative environment hasn't changed,
// we can use the cached environment binding index.
2025-03-16 18:39:57 -05:00
if ( cache . has_environment_binding_index ) {
if ( cache . in_module_environment ) {
auto module = vm . running_execution_context ( ) . script_or_module . get_pointer < GC : : Ref < Module > > ( ) ;
return ( * module ) - > environment ( ) - > get_binding_value_direct ( vm , cache . environment_binding_index ) ;
}
return declarative_record . get_binding_value_direct ( vm , cache . environment_binding_index ) ;
}
2024-07-09 10:10:14 +02:00
}
cache . environment_serial_number = declarative_record . environment_serial_number ( ) ;
2025-10-07 16:47:41 +02:00
auto & identifier = interpreter . get_identifier ( identifier_index ) ;
2024-07-09 10:10:14 +02:00
2025-03-16 17:41:24 -05:00
if ( auto * module = vm . running_execution_context ( ) . script_or_module . get_pointer < GC : : Ref < Module > > ( ) ) {
2024-07-09 10:10:14 +02:00
// 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.
2025-03-16 17:41:24 -05:00
auto & module_environment = * ( * module ) - > environment ( ) ;
2025-03-16 18:39:57 -05:00
Optional < size_t > index ;
if ( TRY ( module_environment . has_binding ( identifier , & index ) ) ) {
if ( index . has_value ( ) ) {
cache . environment_binding_index = static_cast < u32 > ( index . value ( ) ) ;
cache . has_environment_binding_index = true ;
cache . in_module_environment = true ;
return TRY ( module_environment . get_binding_value_direct ( vm , index . value ( ) ) ) ;
}
2024-07-09 10:10:14 +02:00
return TRY ( module_environment . get_binding_value ( vm , identifier , vm . in_strict_mode ( ) ) ) ;
}
}
Optional < size_t > offset ;
if ( TRY ( declarative_record . has_binding ( identifier , & offset ) ) ) {
cache . environment_binding_index = static_cast < u32 > ( offset . value ( ) ) ;
2025-03-16 18:39:57 -05:00
cache . has_environment_binding_index = true ;
cache . in_module_environment = false ;
2024-07-09 10:10:14 +02:00
return TRY ( declarative_record . get_binding_value ( vm , identifier , vm . in_strict_mode ( ) ) ) ;
}
if ( TRY ( binding_object . has_property ( identifier ) ) ) {
2025-09-15 17:23:39 +02:00
CacheableGetPropertyMetadata cacheable_metadata ;
2024-07-09 10:10:14 +02:00
auto value = TRY ( binding_object . internal_get ( identifier , js_undefined ( ) , & cacheable_metadata ) ) ;
2025-09-15 17:23:39 +02:00
if ( cacheable_metadata . type = = CacheableGetPropertyMetadata : : Type : : GetOwnProperty ) {
2025-05-06 13:16:44 +02:00
cache . entries [ 0 ] . shape = shape ;
cache . entries [ 0 ] . property_offset = cacheable_metadata . property_offset . value ( ) ;
2024-07-09 10:10:14 +02:00
}
return value ;
}
return vm . throw_completion < ReferenceError > ( ErrorType : : UnknownIdentifier , identifier ) ;
}
2025-10-10 11:16:02 +02:00
inline ThrowCompletionOr < void > put_by_property_key ( VM & vm , Value base , Value this_value , Value value , Optional < Utf16FlyString const & > const & base_identifier , PropertyKey name , PutKind kind , PropertyLookupCache * caches = nullptr )
2024-07-09 10:10:14 +02:00
{
// Better error message than to_object would give
if ( vm . in_strict_mode ( ) & & base . is_nullish ( ) )
return vm . throw_completion < TypeError > ( ErrorType : : ReferenceNullishSetProperty , name , base . to_string_without_side_effects ( ) ) ;
// a. Let baseObj be ? ToObject(V.[[Base]]).
auto maybe_object = base . to_object ( vm ) ;
if ( maybe_object . is_error ( ) )
return throw_null_or_undefined_property_access ( vm , base , base_identifier , name ) ;
auto object = maybe_object . release_value ( ) ;
2025-10-10 11:16:02 +02:00
if ( kind = = PutKind : : Getter | | kind = = PutKind : : Setter ) {
2024-07-09 10:10:14 +02:00
// The generator should only pass us functions for getters and setters.
VERIFY ( value . is_function ( ) ) ;
}
switch ( kind ) {
2025-10-10 11:16:02 +02:00
case PutKind : : Getter : {
2024-07-09 10:10:14 +02:00
auto & function = value . as_function ( ) ;
2025-04-09 22:31:12 +02:00
if ( is < ECMAScriptFunctionObject > ( function ) & & static_cast < ECMAScriptFunctionObject const & > ( function ) . name ( ) . is_empty ( ) )
2025-08-02 19:27:29 -04:00
static_cast < ECMAScriptFunctionObject * > ( & function ) - > set_name ( Utf16String : : formatted ( " get {} " , name ) ) ;
2024-07-09 10:10:14 +02:00
object - > define_direct_accessor ( name , & function , nullptr , Attribute : : Configurable | Attribute : : Enumerable ) ;
break ;
}
2025-10-10 11:16:02 +02:00
case PutKind : : Setter : {
2024-07-09 10:10:14 +02:00
auto & function = value . as_function ( ) ;
2025-04-09 22:31:12 +02:00
if ( is < ECMAScriptFunctionObject > ( function ) & & static_cast < ECMAScriptFunctionObject const & > ( function ) . name ( ) . is_empty ( ) )
2025-08-02 19:27:29 -04:00
static_cast < ECMAScriptFunctionObject * > ( & function ) - > set_name ( Utf16String : : formatted ( " set {} " , name ) ) ;
2024-07-09 10:10:14 +02:00
object - > define_direct_accessor ( name , nullptr , & function , Attribute : : Configurable | Attribute : : Enumerable ) ;
break ;
}
2025-10-10 11:16:02 +02:00
case PutKind : : Normal : {
2025-09-15 17:23:39 +02:00
auto this_value_object = MUST ( this_value . to_object ( vm ) ) ;
auto & from_shape = this_value_object - > shape ( ) ;
2025-05-06 13:16:44 +02:00
if ( caches ) {
2025-05-06 13:58:27 +02:00
for ( auto & cache : caches - > entries ) {
2025-09-15 17:23:39 +02:00
switch ( cache . type ) {
case PropertyLookupCache : : Entry : : Type : : Empty :
break ;
case PropertyLookupCache : : Entry : : Type : : ChangePropertyInPrototypeChain : {
if ( ! cache . prototype )
break ;
2025-05-06 13:58:27 +02:00
// OPTIMIZATION: If the prototype chain hasn't been mutated in a way that would invalidate the cache, we can use it.
bool can_use_cache = [ & ] ( ) - > bool {
if ( & object - > shape ( ) ! = cache . shape )
return false ;
if ( ! cache . prototype_chain_validity )
return false ;
if ( ! cache . prototype_chain_validity - > is_valid ( ) )
return false ;
return true ;
} ( ) ;
if ( can_use_cache ) {
auto value_in_prototype = cache . prototype - > get_direct ( cache . property_offset . value ( ) ) ;
if ( value_in_prototype . is_accessor ( ) ) {
TRY ( call ( vm , value_in_prototype . as_accessor ( ) . setter ( ) , this_value , value ) ) ;
return { } ;
}
2025-05-04 16:00:13 +02:00
}
2025-09-15 17:23:39 +02:00
break ;
}
case PropertyLookupCache : : Entry : : Type : : ChangeOwnProperty : {
if ( cache . shape ! = & object - > shape ( ) )
break ;
2025-05-06 13:58:27 +02:00
auto value_in_object = object - > get_direct ( cache . property_offset . value ( ) ) ;
if ( value_in_object . is_accessor ( ) ) {
TRY ( call ( vm , value_in_object . as_accessor ( ) . setter ( ) , this_value , value ) ) ;
} else {
object - > put_direct ( * cache . property_offset , value ) ;
}
return { } ;
2025-05-04 16:00:13 +02:00
}
2025-09-15 17:23:39 +02:00
case PropertyLookupCache : : Entry : : Type : : AddOwnProperty : {
// OPTIMIZATION: If the object's shape is the same as the one cached before adding the new property, we can
// reuse the resulting shape from the cache.
if ( cache . from_shape ! = & object - > shape ( ) )
break ;
if ( ! cache . shape )
break ;
// The cache is invalid if the prototype chain has been mutated, since such a mutation could have added a setter for the property.
if ( cache . prototype_chain_validity & & ! cache . prototype_chain_validity - > is_valid ( ) )
break ;
object - > unsafe_set_shape ( * cache . shape ) ;
object - > put_direct ( * cache . property_offset , value ) ;
return { } ;
}
default :
VERIFY_NOT_REACHED ( ) ;
}
2025-05-04 16:00:13 +02:00
}
2024-07-09 10:10:14 +02:00
}
2025-09-15 17:23:39 +02:00
CacheableSetPropertyMetadata cacheable_metadata ;
2024-07-09 10:10:14 +02:00
bool succeeded = TRY ( object - > internal_set ( name , value , this_value , & cacheable_metadata ) ) ;
2025-09-15 17:23:39 +02:00
auto get_cache_slot = [ & ] - > PropertyLookupCache : : Entry & {
for ( size_t i = caches - > entries . size ( ) - 1 ; i > = 1 ; - - i ) {
caches - > entries [ i ] = caches - > entries [ i - 1 ] ;
}
caches - > entries [ 0 ] = { } ;
return caches - > entries [ 0 ] ;
} ;
if ( succeeded & & caches & & cacheable_metadata . type = = CacheableSetPropertyMetadata : : Type : : AddOwnProperty ) {
auto & cache = get_cache_slot ( ) ;
cache . type = PropertyLookupCache : : Entry : : Type : : AddOwnProperty ;
cache . from_shape = from_shape ;
cache . property_offset = cacheable_metadata . property_offset . value ( ) ;
cache . shape = & object - > shape ( ) ;
if ( cacheable_metadata . prototype ) {
cache . prototype_chain_validity = * cacheable_metadata . prototype - > shape ( ) . prototype_chain_validity ( ) ;
}
}
2025-05-20 23:42:17 +03:00
// If internal_set() caused object's shape change, we can no longer be sure
// that collected metadata is valid, e.g. if setter in prototype chain added
// property with the same name into the object itself.
2025-09-15 17:23:39 +02:00
if ( succeeded & & caches & & & from_shape = = & object - > shape ( ) ) {
2025-05-06 13:58:27 +02:00
auto & cache = get_cache_slot ( ) ;
2025-09-15 17:23:39 +02:00
switch ( cacheable_metadata . type ) {
case CacheableSetPropertyMetadata : : Type : : AddOwnProperty :
// Something went wrong if we ended up here, because cacheable addition of a new property should've changed the shape.
VERIFY_NOT_REACHED ( ) ;
break ;
case CacheableSetPropertyMetadata : : Type : : ChangeOwnProperty :
cache . type = PropertyLookupCache : : Entry : : Type : : ChangeOwnProperty ;
2025-05-06 13:58:27 +02:00
cache . shape = object - > shape ( ) ;
cache . property_offset = cacheable_metadata . property_offset . value ( ) ;
2025-09-15 17:23:39 +02:00
break ;
case CacheableSetPropertyMetadata : : Type : : ChangePropertyInPrototypeChain :
cache . type = PropertyLookupCache : : Entry : : Type : : ChangePropertyInPrototypeChain ;
2025-05-06 13:58:27 +02:00
cache . shape = object - > shape ( ) ;
cache . property_offset = cacheable_metadata . property_offset . value ( ) ;
cache . prototype = * cacheable_metadata . prototype ;
cache . prototype_chain_validity = * cacheable_metadata . prototype - > shape ( ) . prototype_chain_validity ( ) ;
2025-09-15 17:23:39 +02:00
break ;
case CacheableSetPropertyMetadata : : Type : : NotCacheable :
break ;
default :
VERIFY_NOT_REACHED ( ) ;
2025-05-04 16:00:13 +02:00
}
2024-07-09 10:10:14 +02:00
}
2025-05-04 16:00:13 +02:00
if ( ! succeeded & & vm . in_strict_mode ( ) ) [[unlikely]] {
2024-07-09 10:10:14 +02:00
if ( base . is_object ( ) )
return vm . throw_completion < TypeError > ( ErrorType : : ReferenceNullishSetProperty , name , base . to_string_without_side_effects ( ) ) ;
2024-07-23 10:25:06 +02:00
return vm . throw_completion < TypeError > ( ErrorType : : ReferencePrimitiveSetProperty , name , base . typeof_ ( vm ) - > utf8_string ( ) , base . to_string_without_side_effects ( ) ) ;
2024-07-09 10:10:14 +02:00
}
break ;
}
2025-10-10 11:16:02 +02:00
case PutKind : : Own :
2024-07-09 10:10:14 +02:00
object - > define_direct_property ( name , value , Attribute : : Enumerable | Attribute : : Writable | Attribute : : Configurable ) ;
break ;
2025-10-10 11:16:02 +02:00
case PutKind : : Prototype :
2024-07-09 10:10:14 +02:00
if ( value . is_object ( ) | | value . is_null ( ) )
MUST ( object - > internal_set_prototype_of ( value . is_object ( ) ? & value . as_object ( ) : nullptr ) ) ;
break ;
}
return { } ;
}
inline ThrowCompletionOr < Value > perform_call ( Interpreter & interpreter , Value this_value , Op : : CallType call_type , Value callee , ReadonlySpan < Value > argument_values )
{
auto & vm = interpreter . vm ( ) ;
auto & function = callee . as_function ( ) ;
Value return_value ;
if ( call_type = = Op : : CallType : : DirectEval ) {
if ( callee = = interpreter . realm ( ) . intrinsics ( ) . eval_function ( ) )
2025-04-04 23:16:34 +02:00
return_value = TRY ( perform_eval ( vm , ! argument_values . is_empty ( ) ? argument_values [ 0 ] : js_undefined ( ) , vm . in_strict_mode ( ) ? CallerMode : : Strict : CallerMode : : NonStrict , EvalMode : : Direct ) ) ;
2024-07-09 10:10:14 +02:00
else
return_value = TRY ( JS : : call ( vm , function , this_value , argument_values ) ) ;
} else if ( call_type = = Op : : CallType : : Call )
return_value = TRY ( JS : : call ( vm , function , this_value , argument_values ) ) ;
else
return_value = TRY ( construct ( vm , function , argument_values ) ) ;
return return_value ;
}
static inline Completion throw_type_error_for_callee ( Bytecode : : Interpreter & interpreter , Value callee , StringView callee_type , Optional < StringTableIndex > const & expression_string )
{
auto & vm = interpreter . vm ( ) ;
if ( expression_string . has_value ( ) )
2025-04-05 21:46:37 +02:00
return vm . throw_completion < TypeError > ( ErrorType : : IsNotAEvaluatedFrom , callee . to_string_without_side_effects ( ) , callee_type , interpreter . current_executable ( ) . get_string ( * expression_string ) ) ;
2024-07-09 10:10:14 +02:00
return vm . throw_completion < TypeError > ( ErrorType : : IsNotA , callee . to_string_without_side_effects ( ) , callee_type ) ;
}
inline ThrowCompletionOr < void > throw_if_needed_for_call ( Interpreter & interpreter , Value callee , Op : : CallType call_type , Optional < StringTableIndex > const & expression_string )
{
if ( ( call_type = = Op : : CallType : : Call | | call_type = = Op : : CallType : : DirectEval )
& & ! callee . is_function ( ) )
return throw_type_error_for_callee ( interpreter , callee , " function " sv , expression_string ) ;
if ( call_type = = Op : : CallType : : Construct & & ! callee . is_constructor ( ) )
return throw_type_error_for_callee ( interpreter , callee , " constructor " sv , expression_string ) ;
return { } ;
}
2025-04-29 16:08:42 +02:00
inline Value new_function ( VM & vm , FunctionNode const & function_node , Optional < IdentifierTableIndex > const & lhs_name , Optional < Operand > const & home_object )
2024-07-09 10:10:14 +02:00
{
Value value ;
if ( ! function_node . has_name ( ) ) {
2025-08-02 19:27:29 -04:00
Utf16FlyString name ;
2024-07-09 10:10:14 +02:00
if ( lhs_name . has_value ( ) )
2025-04-29 16:08:42 +02:00
name = vm . bytecode_interpreter ( ) . current_executable ( ) . get_identifier ( lhs_name . value ( ) ) ;
2024-07-09 10:10:14 +02:00
value = function_node . instantiate_ordinary_function_expression ( vm , name ) ;
} else {
2025-04-07 21:08:55 +02:00
value = ECMAScriptFunctionObject : : create_from_function_node (
function_node ,
2025-08-06 11:12:58 -04:00
function_node . name ( ) ,
2025-04-07 21:08:55 +02:00
* vm . current_realm ( ) ,
vm . lexical_environment ( ) ,
vm . running_execution_context ( ) . private_environment ) ;
2024-07-09 10:10:14 +02:00
}
if ( home_object . has_value ( ) ) {
2025-04-29 16:08:42 +02:00
auto home_object_value = vm . bytecode_interpreter ( ) . get ( home_object . value ( ) ) ;
2024-07-09 10:10:14 +02:00
static_cast < ECMAScriptFunctionObject & > ( value . as_function ( ) ) . set_home_object ( & home_object_value . as_object ( ) ) ;
}
return value ;
}
2025-10-10 11:16:02 +02:00
inline ThrowCompletionOr < void > put_by_value ( VM & vm , Value base , Optional < Utf16FlyString const & > const & base_identifier , Value property_key_value , Value value , PutKind kind )
2024-07-09 10:10:14 +02:00
{
// OPTIMIZATION: Fast path for simple Int32 indexes in array-like objects.
2025-10-10 11:16:02 +02:00
if ( ( kind = = PutKind : : Normal | | kind = = PutKind : : Own )
2024-07-09 10:10:14 +02:00
& & 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 ( ) ) ;
// For "non-typed arrays":
if ( storage
& & storage - > is_simple_storage ( )
& & ! object . may_interfere_with_indexed_property_access ( ) ) {
auto maybe_value = storage - > get ( index ) ;
if ( maybe_value . has_value ( ) ) {
auto existing_value = maybe_value - > value ;
if ( ! existing_value . is_accessor ( ) ) {
storage - > put ( index , value ) ;
return { } ;
}
}
}
// For typed arrays:
if ( object . is_typed_array ( ) ) {
auto & typed_array = static_cast < TypedArrayBase & > ( object ) ;
auto canonical_index = CanonicalIndex { CanonicalIndex : : Type : : Index , index } ;
2024-11-18 01:42:36 +01:00
if ( is_valid_integer_index ( typed_array , canonical_index ) ) {
if ( value . is_int32 ( ) ) {
switch ( typed_array . kind ( ) ) {
case TypedArrayBase : : Kind : : Uint8Array :
fast_typed_array_set_element < u8 > ( typed_array , index , static_cast < u8 > ( value . as_i32 ( ) ) ) ;
return { } ;
case TypedArrayBase : : Kind : : Uint16Array :
fast_typed_array_set_element < u16 > ( typed_array , index , static_cast < u16 > ( value . as_i32 ( ) ) ) ;
return { } ;
case TypedArrayBase : : Kind : : Uint32Array :
fast_typed_array_set_element < u32 > ( typed_array , index , static_cast < u32 > ( value . as_i32 ( ) ) ) ;
return { } ;
case TypedArrayBase : : Kind : : Int8Array :
fast_typed_array_set_element < i8 > ( typed_array , index , static_cast < i8 > ( value . as_i32 ( ) ) ) ;
return { } ;
case TypedArrayBase : : Kind : : Int16Array :
fast_typed_array_set_element < i16 > ( typed_array , index , static_cast < i16 > ( value . as_i32 ( ) ) ) ;
return { } ;
case TypedArrayBase : : Kind : : Int32Array :
fast_typed_array_set_element < i32 > ( typed_array , index , value . as_i32 ( ) ) ;
return { } ;
case TypedArrayBase : : Kind : : Uint8ClampedArray :
fast_typed_array_set_element < u8 > ( typed_array , index , clamp ( value . as_i32 ( ) , 0 , 255 ) ) ;
return { } ;
default :
break ;
}
} else if ( value . is_double ( ) ) {
switch ( typed_array . kind ( ) ) {
case TypedArrayBase : : Kind : : Float16Array :
fast_typed_array_set_element < f16 > ( typed_array , index , static_cast < f16 > ( value . as_double ( ) ) ) ;
return { } ;
case TypedArrayBase : : Kind : : Float32Array :
fast_typed_array_set_element < float > ( typed_array , index , static_cast < float > ( value . as_double ( ) ) ) ;
return { } ;
case TypedArrayBase : : Kind : : Float64Array :
fast_typed_array_set_element < double > ( typed_array , index , value . as_double ( ) ) ;
return { } ;
2025-05-13 14:12:50 +02:00
case TypedArrayBase : : Kind : : Int8Array :
fast_typed_array_set_element < i8 > ( typed_array , index , MUST ( value . to_i8 ( vm ) ) ) ;
return { } ;
case TypedArrayBase : : Kind : : Int16Array :
fast_typed_array_set_element < i16 > ( typed_array , index , MUST ( value . to_i16 ( vm ) ) ) ;
return { } ;
case TypedArrayBase : : Kind : : Int32Array :
fast_typed_array_set_element < i32 > ( typed_array , index , MUST ( value . to_i32 ( vm ) ) ) ;
return { } ;
case TypedArrayBase : : Kind : : Uint8Array :
fast_typed_array_set_element < u8 > ( typed_array , index , MUST ( value . to_u8 ( vm ) ) ) ;
return { } ;
case TypedArrayBase : : Kind : : Uint16Array :
fast_typed_array_set_element < u16 > ( typed_array , index , MUST ( value . to_u16 ( vm ) ) ) ;
return { } ;
case TypedArrayBase : : Kind : : Uint32Array :
fast_typed_array_set_element < u32 > ( typed_array , index , MUST ( value . to_u32 ( vm ) ) ) ;
return { } ;
2024-11-18 01:42:36 +01:00
default :
break ;
}
2024-07-09 10:10:14 +02:00
}
2024-11-18 01:42:36 +01:00
// FIXME: Support more TypedArray kinds.
2024-07-09 10:10:14 +02:00
}
if ( typed_array . kind ( ) = = TypedArrayBase : : Kind : : Uint32Array & & value . is_integral_number ( ) ) {
auto integer = value . as_double ( ) ;
if ( AK : : is_within_range < u32 > ( integer ) & & is_valid_integer_index ( typed_array , canonical_index ) ) {
fast_typed_array_set_element < u32 > ( typed_array , index , static_cast < u32 > ( integer ) ) ;
return { } ;
}
}
switch ( typed_array . kind ( ) ) {
# define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
case TypedArrayBase : : Kind : : ClassName : \
return typed_array_set_element < Type > ( typed_array , canonical_index , value ) ;
JS_ENUMERATE_TYPED_ARRAYS
# undef __JS_ENUMERATE
}
return { } ;
}
}
2024-11-01 22:00:32 +01:00
auto property_key = TRY ( property_key_value . to_property_key ( vm ) ) ;
2024-07-09 10:10:14 +02:00
TRY ( put_by_property_key ( vm , base , base , value , base_identifier , property_key , kind ) ) ;
return { } ;
}
struct CalleeAndThis {
Value callee ;
Value this_value ;
} ;
2025-08-02 19:27:29 -04:00
inline ThrowCompletionOr < CalleeAndThis > get_callee_and_this_from_environment ( Bytecode : : Interpreter & interpreter , Utf16FlyString const & name , EnvironmentCoordinate & cache )
2024-07-09 10:10:14 +02:00
{
auto & vm = interpreter . vm ( ) ;
Value callee = js_undefined ( ) ;
Value this_value = js_undefined ( ) ;
2025-10-07 16:37:22 +02:00
if ( cache . is_valid ( ) ) [[likely]] {
2024-07-09 10:10:14 +02:00
auto const * environment = interpreter . running_execution_context ( ) . lexical_environment . ptr ( ) ;
for ( size_t i = 0 ; i < cache . hops ; + + i )
environment = environment - > outer_environment ( ) ;
2025-10-07 16:37:22 +02:00
if ( ! environment - > is_permanently_screwed_by_eval ( ) ) [[likely]] {
2024-07-09 10:10:14 +02:00
callee = TRY ( static_cast < DeclarativeEnvironment const & > ( * environment ) . get_binding_value_direct ( vm , cache . index ) ) ;
this_value = js_undefined ( ) ;
if ( auto base_object = environment - > with_base_object ( ) )
this_value = base_object ;
return CalleeAndThis {
. callee = callee ,
. this_value = this_value ,
} ;
}
cache = { } ;
}
auto reference = TRY ( vm . resolve_binding ( name ) ) ;
if ( reference . environment_coordinate ( ) . has_value ( ) )
cache = reference . environment_coordinate ( ) . value ( ) ;
callee = TRY ( reference . get_value ( vm ) ) ;
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 ;
}
}
return CalleeAndThis {
. callee = callee ,
. this_value = this_value ,
} ;
}
// 13.2.7.3 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-regular-expression-literals-runtime-semantics-evaluation
2025-08-06 11:28:18 -04:00
inline Value new_regexp ( VM & vm , ParsedRegex const & parsed_regex , Utf16String pattern , Utf16String flags )
2024-07-09 10:10:14 +02:00
{
// 1. Let pattern be CodePointsToString(BodyText of RegularExpressionLiteral).
// 2. Let flags be CodePointsToString(FlagText of RegularExpressionLiteral).
// 3. Return ! RegExpCreate(pattern, flags).
auto & realm = * vm . current_realm ( ) ;
2025-03-18 18:08:02 -05:00
Regex < ECMA262 > regex ( parsed_regex . regex , parsed_regex . pattern . to_byte_string ( ) , parsed_regex . flags ) ;
2024-07-09 10:10:14 +02:00
// NOTE: We bypass RegExpCreate and subsequently RegExpAlloc as an optimization to use the already parsed values.
2025-08-06 11:28:18 -04:00
auto regexp_object = RegExpObject : : create ( realm , move ( regex ) , move ( pattern ) , move ( flags ) ) ;
2024-07-09 10:10:14 +02:00
// RegExpAlloc has these two steps from the 'Legacy RegExp features' proposal.
regexp_object - > set_realm ( 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 ) ;
return regexp_object ;
}
2025-08-02 19:27:29 -04:00
inline ThrowCompletionOr < void > create_variable ( VM & vm , Utf16FlyString const & name , Op : : EnvironmentMode mode , bool is_global , bool is_immutable , bool is_strict )
2024-07-09 10:10:14 +02:00
{
if ( mode = = Op : : EnvironmentMode : : Lexical ) {
VERIFY ( ! 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 ( is_immutable )
return vm . lexical_environment ( ) - > create_immutable_binding ( vm , name , is_strict ) ;
return vm . lexical_environment ( ) - > create_mutable_binding ( vm , name , is_strict ) ;
}
if ( ! is_global ) {
if ( is_immutable )
return vm . variable_environment ( ) - > create_immutable_binding ( vm , name , is_strict ) ;
return vm . variable_environment ( ) - > create_mutable_binding ( vm , name , is_strict ) ;
}
// 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.
2025-01-21 09:12:05 -05:00
return as < GlobalEnvironment > ( vm . variable_environment ( ) ) - > create_global_var_binding ( name , false ) ;
2024-07-09 10:10:14 +02:00
}
inline ThrowCompletionOr < ECMAScriptFunctionObject * > new_class ( VM & vm , Value super_class , ClassExpression const & class_expression , Optional < IdentifierTableIndex > const & lhs_name , ReadonlySpan < Value > element_keys )
{
auto & interpreter = vm . bytecode_interpreter ( ) ;
// NOTE: NewClass expects classEnv to be active lexical environment
auto * class_environment = vm . lexical_environment ( ) ;
vm . running_execution_context ( ) . lexical_environment = vm . running_execution_context ( ) . saved_lexical_environments . take_last ( ) ;
2025-08-02 19:27:29 -04:00
Optional < Utf16FlyString > binding_name ;
Utf16FlyString class_name ;
2024-07-09 10:10:14 +02:00
if ( ! class_expression . has_name ( ) & & lhs_name . has_value ( ) ) {
2025-10-07 16:47:41 +02:00
class_name = interpreter . get_identifier ( lhs_name . value ( ) ) ;
2024-07-09 10:10:14 +02:00
} else {
2025-08-06 11:12:58 -04:00
class_name = class_expression . name ( ) ;
binding_name = class_name ;
2024-07-09 10:10:14 +02:00
}
return TRY ( class_expression . create_class_constructor ( vm , class_environment , vm . lexical_environment ( ) , super_class , element_keys , binding_name , class_name ) ) ;
}
2024-11-15 04:01:23 +13:00
inline ThrowCompletionOr < GC : : Ref < Array > > iterator_to_array ( VM & vm , Value iterator )
2024-07-09 10:10:14 +02:00
{
2025-03-22 11:46:54 -05:00
auto & iterator_record = static_cast < IteratorRecord & > ( iterator . as_cell ( ) ) ;
2024-07-09 10:10:14 +02:00
auto array = MUST ( Array : : create ( * vm . current_realm ( ) , 0 ) ) ;
size_t index = 0 ;
while ( true ) {
auto value = TRY ( iterator_step_value ( vm , iterator_record ) ) ;
if ( ! value . has_value ( ) )
return array ;
MUST ( array - > create_data_property_or_throw ( index , value . release_value ( ) ) ) ;
index + + ;
}
}
inline ThrowCompletionOr < void > append ( VM & vm , Value lhs , Value rhs , bool is_spread )
{
// 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.
// Note: We know from codegen, that lhs is a plain array with only indexed properties
auto & lhs_array = lhs . as_array ( ) ;
auto lhs_size = lhs_array . indexed_properties ( ) . array_like_size ( ) ;
if ( is_spread ) {
// ...rhs
size_t i = lhs_size ;
TRY ( get_iterator_values ( vm , rhs , [ & i , & lhs_array ] ( Value iterator_value ) - > Optional < Completion > {
lhs_array . indexed_properties ( ) . put ( i , iterator_value , default_attributes ) ;
+ + i ;
return { } ;
} ) ) ;
} else {
lhs_array . indexed_properties ( ) . put ( lhs_size , rhs , default_attributes ) ;
}
return { } ;
}
inline ThrowCompletionOr < Value > delete_by_id ( Bytecode : : Interpreter & interpreter , Value base , IdentifierTableIndex property )
{
auto & vm = interpreter . vm ( ) ;
2025-10-07 16:47:41 +02:00
auto const & identifier = interpreter . get_identifier ( property ) ;
2024-07-09 10:10:14 +02:00
bool strict = vm . in_strict_mode ( ) ;
auto reference = Reference { base , identifier , { } , strict } ;
return TRY ( reference . delete_ ( vm ) ) ;
}
inline ThrowCompletionOr < Value > delete_by_value ( Bytecode : : Interpreter & interpreter , Value base , Value property_key_value )
{
auto & vm = interpreter . vm ( ) ;
auto property_key = TRY ( property_key_value . to_property_key ( vm ) ) ;
bool strict = vm . in_strict_mode ( ) ;
auto reference = Reference { base , property_key , { } , strict } ;
return Value ( TRY ( reference . delete_ ( vm ) ) ) ;
}
inline ThrowCompletionOr < Value > delete_by_value_with_this ( Bytecode : : Interpreter & interpreter , Value base , Value property_key_value , Value this_value )
{
auto & vm = interpreter . vm ( ) ;
auto property_key = TRY ( property_key_value . to_property_key ( vm ) ) ;
bool strict = vm . in_strict_mode ( ) ;
auto reference = Reference { base , property_key , this_value , strict } ;
return Value ( TRY ( reference . delete_ ( vm ) ) ) ;
}
2025-05-24 23:46:13 +02:00
class JS_API PropertyNameIterator final
2025-05-03 10:17:11 +02:00
: public Object
, public BuiltinIterator {
JS_OBJECT ( PropertyNameIterator , Object ) ;
GC_DECLARE_ALLOCATOR ( PropertyNameIterator ) ;
public :
virtual ~ PropertyNameIterator ( ) override = default ;
2025-06-01 18:44:18 +02:00
BuiltinIterator * as_builtin_iterator_if_next_is_not_redefined ( IteratorRecord const & ) override { return this ; }
LibJS: Fast-path own-property enumeration and reduce descriptor lookups
Before this change, PropertyNameIterator (used by for..in) and
`Object::enumerable_own_property_names()` (used by `Object.keys()`,
`Object.values()`, and `Object.entries()`) enumerated an object's own
enumerable properties exactly as the spec prescribes:
- Call `internal_own_property_keys()`, allocating a list of JS::Value
keys.
- For each key, call internal_get_own_property() to obtain a
descriptor and check `[[Enumerable]]`.
While that is required in the general case (e.g. for Proxy objects or
platform/exotic objects that override `[[OwnPropertyKeys]]`), it's
overkill for ordinary JS objects that store their own properties in the
shape table and indexed-properties storage.
This change introduces `for_each_own_property_with_enumerability()`,
which, for objects where
`eligible_for_own_property_enumeration_fast_path()` is `true`, lets us
read the enumerability directly from shape metadata (and from
indexed-properties storage) without a per-property descriptor lookup.
When we cannot avoid `internal_get_own_property()`, we still
benefit by skipping the temporary `Vector<Value>` of keys and avoiding
the unnecessary round-trip between PropertyKey and Value.
2025-09-19 16:49:53 +02:00
ThrowCompletionOr < void > next ( VM & vm , bool & done , Value & value ) override
2025-05-03 10:17:11 +02:00
{
while ( true ) {
2025-05-03 21:12:00 +02:00
if ( m_iterator = = m_properties . end ( ) ) {
2025-05-03 10:17:11 +02:00
done = true ;
return { } ;
}
2025-05-03 21:12:00 +02:00
auto const & entry = * m_iterator ;
ScopeGuard remove_first = [ & ] { + + m_iterator ; } ;
2025-05-03 10:17:11 +02:00
// If the property is deleted, don't include it (invariant no. 2)
2025-09-20 19:16:31 +02:00
if ( ! TRY ( m_object - > has_property ( entry ) ) )
2025-05-03 10:17:11 +02:00
continue ;
done = false ;
2025-09-20 19:16:31 +02:00
value = entry . to_value ( vm ) ;
2025-05-03 10:17:11 +02:00
return { } ;
}
}
private :
2025-09-20 19:16:31 +02:00
PropertyNameIterator ( JS : : Realm & realm , GC : : Ref < Object > object , Vector < PropertyKey > properties )
2025-05-03 10:17:11 +02:00
: Object ( realm , nullptr )
, m_object ( object )
, m_properties ( move ( properties ) )
2025-05-03 21:12:00 +02:00
, m_iterator ( m_properties . begin ( ) )
2025-05-03 10:17:11 +02:00
{
}
virtual void visit_edges ( Visitor & visitor ) override
{
Base : : visit_edges ( visitor ) ;
visitor . visit ( m_object ) ;
}
GC : : Ref < Object > m_object ;
2025-09-20 19:16:31 +02:00
Vector < PropertyKey > m_properties ;
2025-05-03 21:12:00 +02:00
decltype ( m_properties . begin ( ) ) m_iterator ;
2025-05-03 10:17:11 +02:00
} ;
GC_DEFINE_ALLOCATOR ( PropertyNameIterator ) ;
2024-07-09 10:10:14 +02:00
// 14.7.5.9 EnumerateObjectProperties ( O ), https://tc39.es/ecma262/#sec-enumerate-object-properties
2025-05-03 10:17:11 +02:00
inline ThrowCompletionOr < Value > get_object_property_iterator ( Interpreter & interpreter , Value value )
2024-07-09 10:10:14 +02:00
{
// 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
2025-05-03 10:17:11 +02:00
auto & vm = interpreter . vm ( ) ;
2024-07-09 10:10:14 +02:00
// Invariant 3 effectively allows the implementation to ignore newly added keys, and we do so (similar to other implementations).
auto object = TRY ( value . 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.
2025-03-19 17:08:24 -05:00
LibJS: Fast-path own-property enumeration and reduce descriptor lookups
Before this change, PropertyNameIterator (used by for..in) and
`Object::enumerable_own_property_names()` (used by `Object.keys()`,
`Object.values()`, and `Object.entries()`) enumerated an object's own
enumerable properties exactly as the spec prescribes:
- Call `internal_own_property_keys()`, allocating a list of JS::Value
keys.
- For each key, call internal_get_own_property() to obtain a
descriptor and check `[[Enumerable]]`.
While that is required in the general case (e.g. for Proxy objects or
platform/exotic objects that override `[[OwnPropertyKeys]]`), it's
overkill for ordinary JS objects that store their own properties in the
shape table and indexed-properties storage.
This change introduces `for_each_own_property_with_enumerability()`,
which, for objects where
`eligible_for_own_property_enumeration_fast_path()` is `true`, lets us
read the enumerability directly from shape metadata (and from
indexed-properties storage) without a per-property descriptor lookup.
When we cannot avoid `internal_get_own_property()`, we still
benefit by skipping the temporary `Vector<Value>` of keys and avoiding
the unnecessary round-trip between PropertyKey and Value.
2025-09-19 16:49:53 +02:00
size_t estimated_properties_count = 0 ;
2024-11-15 04:01:23 +13:00
HashTable < GC : : Ref < Object > > seen_objects ;
for ( auto object_to_check = GC : : Ptr { object . ptr ( ) } ; object_to_check & & ! seen_objects . contains ( * object_to_check ) ; object_to_check = TRY ( object_to_check - > internal_get_prototype_of ( ) ) ) {
2024-07-09 10:10:14 +02:00
seen_objects . set ( * object_to_check ) ;
LibJS: Fast-path own-property enumeration and reduce descriptor lookups
Before this change, PropertyNameIterator (used by for..in) and
`Object::enumerable_own_property_names()` (used by `Object.keys()`,
`Object.values()`, and `Object.entries()`) enumerated an object's own
enumerable properties exactly as the spec prescribes:
- Call `internal_own_property_keys()`, allocating a list of JS::Value
keys.
- For each key, call internal_get_own_property() to obtain a
descriptor and check `[[Enumerable]]`.
While that is required in the general case (e.g. for Proxy objects or
platform/exotic objects that override `[[OwnPropertyKeys]]`), it's
overkill for ordinary JS objects that store their own properties in the
shape table and indexed-properties storage.
This change introduces `for_each_own_property_with_enumerability()`,
which, for objects where
`eligible_for_own_property_enumeration_fast_path()` is `true`, lets us
read the enumerability directly from shape metadata (and from
indexed-properties storage) without a per-property descriptor lookup.
When we cannot avoid `internal_get_own_property()`, we still
benefit by skipping the temporary `Vector<Value>` of keys and avoiding
the unnecessary round-trip between PropertyKey and Value.
2025-09-19 16:49:53 +02:00
estimated_properties_count + = object_to_check - > own_properties_count ( ) ;
}
seen_objects . clear_with_capacity ( ) ;
2025-09-20 19:16:31 +02:00
Vector < PropertyKey > properties ;
LibJS: Fast-path own-property enumeration and reduce descriptor lookups
Before this change, PropertyNameIterator (used by for..in) and
`Object::enumerable_own_property_names()` (used by `Object.keys()`,
`Object.values()`, and `Object.entries()`) enumerated an object's own
enumerable properties exactly as the spec prescribes:
- Call `internal_own_property_keys()`, allocating a list of JS::Value
keys.
- For each key, call internal_get_own_property() to obtain a
descriptor and check `[[Enumerable]]`.
While that is required in the general case (e.g. for Proxy objects or
platform/exotic objects that override `[[OwnPropertyKeys]]`), it's
overkill for ordinary JS objects that store their own properties in the
shape table and indexed-properties storage.
This change introduces `for_each_own_property_with_enumerability()`,
which, for objects where
`eligible_for_own_property_enumeration_fast_path()` is `true`, lets us
read the enumerability directly from shape metadata (and from
indexed-properties storage) without a per-property descriptor lookup.
When we cannot avoid `internal_get_own_property()`, we still
benefit by skipping the temporary `Vector<Value>` of keys and avoiding
the unnecessary round-trip between PropertyKey and Value.
2025-09-19 16:49:53 +02:00
properties . ensure_capacity ( estimated_properties_count ) ;
2024-07-09 10:10:14 +02:00
2025-09-20 19:16:31 +02:00
HashTable < PropertyKey > seen_non_enumerable_properties ;
Optional < HashTable < PropertyKey > > seen_properties ;
auto ensure_seen_properties = [ & ] {
if ( seen_properties . has_value ( ) )
return ;
seen_properties = HashTable < PropertyKey > { } ;
seen_properties - > ensure_capacity ( properties . size ( ) ) ;
for ( auto const & property : properties )
seen_properties - > set ( property ) ;
} ;
LibJS: Fast-path own-property enumeration and reduce descriptor lookups
Before this change, PropertyNameIterator (used by for..in) and
`Object::enumerable_own_property_names()` (used by `Object.keys()`,
`Object.values()`, and `Object.entries()`) enumerated an object's own
enumerable properties exactly as the spec prescribes:
- Call `internal_own_property_keys()`, allocating a list of JS::Value
keys.
- For each key, call internal_get_own_property() to obtain a
descriptor and check `[[Enumerable]]`.
While that is required in the general case (e.g. for Proxy objects or
platform/exotic objects that override `[[OwnPropertyKeys]]`), it's
overkill for ordinary JS objects that store their own properties in the
shape table and indexed-properties storage.
This change introduces `for_each_own_property_with_enumerability()`,
which, for objects where
`eligible_for_own_property_enumeration_fast_path()` is `true`, lets us
read the enumerability directly from shape metadata (and from
indexed-properties storage) without a per-property descriptor lookup.
When we cannot avoid `internal_get_own_property()`, we still
benefit by skipping the temporary `Vector<Value>` of keys and avoiding
the unnecessary round-trip between PropertyKey and Value.
2025-09-19 16:49:53 +02:00
// Collect all keys immediately (invariant no. 5)
2025-09-20 19:16:31 +02:00
bool in_prototype_chain = false ;
LibJS: Fast-path own-property enumeration and reduce descriptor lookups
Before this change, PropertyNameIterator (used by for..in) and
`Object::enumerable_own_property_names()` (used by `Object.keys()`,
`Object.values()`, and `Object.entries()`) enumerated an object's own
enumerable properties exactly as the spec prescribes:
- Call `internal_own_property_keys()`, allocating a list of JS::Value
keys.
- For each key, call internal_get_own_property() to obtain a
descriptor and check `[[Enumerable]]`.
While that is required in the general case (e.g. for Proxy objects or
platform/exotic objects that override `[[OwnPropertyKeys]]`), it's
overkill for ordinary JS objects that store their own properties in the
shape table and indexed-properties storage.
This change introduces `for_each_own_property_with_enumerability()`,
which, for objects where
`eligible_for_own_property_enumeration_fast_path()` is `true`, lets us
read the enumerability directly from shape metadata (and from
indexed-properties storage) without a per-property descriptor lookup.
When we cannot avoid `internal_get_own_property()`, we still
benefit by skipping the temporary `Vector<Value>` of keys and avoiding
the unnecessary round-trip between PropertyKey and Value.
2025-09-19 16:49:53 +02:00
for ( auto object_to_check = GC : : Ptr { 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 ) ;
TRY ( object_to_check - > for_each_own_property_with_enumerability ( [ & ] ( PropertyKey const & property_key , bool enumerable ) - > ThrowCompletionOr < void > {
2025-09-20 19:16:31 +02:00
if ( ! enumerable )
seen_non_enumerable_properties . set ( property_key ) ;
if ( in_prototype_chain & & enumerable ) {
if ( seen_non_enumerable_properties . contains ( property_key ) )
return { } ;
ensure_seen_properties ( ) ;
if ( seen_properties - > contains ( property_key ) )
return { } ;
}
if ( enumerable )
properties . append ( property_key ) ;
if ( seen_properties . has_value ( ) )
seen_properties - > set ( property_key ) ;
LibJS: Fast-path own-property enumeration and reduce descriptor lookups
Before this change, PropertyNameIterator (used by for..in) and
`Object::enumerable_own_property_names()` (used by `Object.keys()`,
`Object.values()`, and `Object.entries()`) enumerated an object's own
enumerable properties exactly as the spec prescribes:
- Call `internal_own_property_keys()`, allocating a list of JS::Value
keys.
- For each key, call internal_get_own_property() to obtain a
descriptor and check `[[Enumerable]]`.
While that is required in the general case (e.g. for Proxy objects or
platform/exotic objects that override `[[OwnPropertyKeys]]`), it's
overkill for ordinary JS objects that store their own properties in the
shape table and indexed-properties storage.
This change introduces `for_each_own_property_with_enumerability()`,
which, for objects where
`eligible_for_own_property_enumeration_fast_path()` is `true`, lets us
read the enumerability directly from shape metadata (and from
indexed-properties storage) without a per-property descriptor lookup.
When we cannot avoid `internal_get_own_property()`, we still
benefit by skipping the temporary `Vector<Value>` of keys and avoiding
the unnecessary round-trip between PropertyKey and Value.
2025-09-19 16:49:53 +02:00
return { } ;
} ) ) ;
2025-09-20 19:16:31 +02:00
in_prototype_chain = true ;
2024-07-09 10:10:14 +02:00
}
2025-03-19 17:08:24 -05:00
2025-05-03 10:17:11 +02:00
auto iterator = interpreter . realm ( ) . create < PropertyNameIterator > ( interpreter . realm ( ) , object , move ( properties ) ) ;
2025-03-20 13:19:28 -05:00
2025-05-03 10:17:11 +02:00
return vm . heap ( ) . allocate < IteratorRecord > ( iterator , js_undefined ( ) , false ) ;
2024-07-09 10:10:14 +02:00
}
2023-09-27 10:10:00 +02:00
2023-12-16 17:49:34 +03:30
ByteString Instruction : : to_byte_string ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
# define __BYTECODE_OP(op) \
case Instruction : : Type : : op : \
2023-12-16 17:49:34 +03:30
return static_cast < Bytecode : : Op : : op const & > ( * this ) . to_byte_string_impl ( executable ) ;
2023-09-27 10:10:00 +02:00
switch ( type ( ) ) {
ENUMERATE_BYTECODE_OPS ( __BYTECODE_OP )
default :
VERIFY_NOT_REACHED ( ) ;
}
# undef __BYTECODE_OP
}
}
namespace JS : : Bytecode : : Op {
2024-02-04 08:00:54 +01:00
static void dump_object ( Object & o , HashTable < Object const * > & seen , int indent = 0 )
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
if ( seen . contains ( & o ) )
return ;
seen . set ( & o ) ;
for ( auto & it : o . shape ( ) . property_table ( ) ) {
auto value = o . get_direct ( it . value . offset ) ;
2025-05-15 17:14:00 +03:00
dbgln ( " {} {} -> {} " , String : : repeated ( ' ' , indent ) . release_value ( ) , it . key . to_string ( ) , value ) ;
2024-02-04 08:00:54 +01:00
if ( value . is_object ( ) ) {
dump_object ( value . as_object ( ) , seen , indent + 2 ) ;
}
}
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
void Dump : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
auto value = interpreter . get ( m_value ) ;
2024-02-04 08:00:54 +01:00
dbgln ( " (DUMP) {}: {} " , m_text , value ) ;
if ( value . is_object ( ) ) {
HashTable < Object const * > seen ;
dump_object ( value . as_object ( ) , seen ) ;
}
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
# define JS_DEFINE_EXECUTE_FOR_COMMON_BINARY_OP(OpTitleCase, op_snake_case) \
ThrowCompletionOr < void > OpTitleCase : : execute_impl ( Bytecode : : Interpreter & interpreter ) const \
{ \
auto & vm = interpreter . vm ( ) ; \
auto lhs = interpreter . get ( m_lhs ) ; \
auto rhs = interpreter . get ( m_rhs ) ; \
2025-05-08 15:19:35 +12:00
interpreter . set ( m_dst , Value { TRY ( op_snake_case ( vm , lhs , rhs ) ) } ) ; \
2025-04-29 16:08:42 +02:00
return { } ; \
2023-09-27 10:10:00 +02:00
}
2024-02-20 11:59:46 +01:00
# define JS_DEFINE_TO_BYTE_STRING_FOR_COMMON_BINARY_OP(OpTitleCase, op_snake_case) \
ByteString OpTitleCase : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const \
{ \
return ByteString : : formatted ( # OpTitleCase " {}, {}, {} " , \
format_operand ( " dst " sv , m_dst , executable ) , \
format_operand ( " lhs " sv , m_lhs , executable ) , \
format_operand ( " rhs " sv , m_rhs , executable ) ) ; \
}
JS_ENUMERATE_COMMON_BINARY_OPS_WITHOUT_FAST_PATH ( JS_DEFINE_EXECUTE_FOR_COMMON_BINARY_OP )
JS_ENUMERATE_COMMON_BINARY_OPS_WITHOUT_FAST_PATH ( JS_DEFINE_TO_BYTE_STRING_FOR_COMMON_BINARY_OP )
JS_ENUMERATE_COMMON_BINARY_OPS_WITH_FAST_PATH ( JS_DEFINE_TO_BYTE_STRING_FOR_COMMON_BINARY_OP )
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > Add : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-02-20 11:59:46 +01:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto const lhs = interpreter . get ( m_lhs ) ;
auto const rhs = interpreter . get ( m_rhs ) ;
2024-02-20 11:59:46 +01:00
if ( lhs . is_number ( ) & & rhs . is_number ( ) ) {
if ( lhs . is_int32 ( ) & & rhs . is_int32 ( ) ) {
if ( ! Checked < i32 > : : addition_would_overflow ( lhs . as_i32 ( ) , rhs . as_i32 ( ) ) ) {
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_i32 ( ) + rhs . as_i32 ( ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-10-07 12:08:06 +02:00
auto result = static_cast < i64 > ( lhs . as_i32 ( ) ) + static_cast < i64 > ( rhs . as_i32 ( ) ) ;
interpreter . set ( m_dst , Value ( result , Value : : CannotFitInInt32 : : Indeed ) ) ;
return { } ;
2024-02-20 11:59:46 +01:00
}
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_double ( ) + rhs . as_double ( ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , TRY ( add ( vm , lhs , rhs ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > Mul : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-02-20 11:59:46 +01:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto const lhs = interpreter . get ( m_lhs ) ;
auto const rhs = interpreter . get ( m_rhs ) ;
2024-02-20 11:59:46 +01:00
if ( lhs . is_number ( ) & & rhs . is_number ( ) ) {
if ( lhs . is_int32 ( ) & & rhs . is_int32 ( ) ) {
if ( ! Checked < i32 > : : multiplication_would_overflow ( lhs . as_i32 ( ) , rhs . as_i32 ( ) ) ) {
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_i32 ( ) * rhs . as_i32 ( ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-10-07 12:08:06 +02:00
auto result = static_cast < i64 > ( lhs . as_i32 ( ) ) * static_cast < i64 > ( rhs . as_i32 ( ) ) ;
interpreter . set ( m_dst , Value ( result , Value : : CannotFitInInt32 : : Indeed ) ) ;
return { } ;
2024-02-20 11:59:46 +01:00
}
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_double ( ) * rhs . as_double ( ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , TRY ( mul ( vm , lhs , rhs ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > Sub : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-02-20 11:59:46 +01:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto const lhs = interpreter . get ( m_lhs ) ;
auto const rhs = interpreter . get ( m_rhs ) ;
2024-02-20 11:59:46 +01:00
if ( lhs . is_number ( ) & & rhs . is_number ( ) ) {
if ( lhs . is_int32 ( ) & & rhs . is_int32 ( ) ) {
2024-05-18 10:58:11 +02:00
if ( ! Checked < i32 > : : subtraction_would_overflow ( lhs . as_i32 ( ) , rhs . as_i32 ( ) ) ) {
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_i32 ( ) - rhs . as_i32 ( ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-10-07 12:08:06 +02:00
auto result = static_cast < i64 > ( lhs . as_i32 ( ) ) - static_cast < i64 > ( rhs . as_i32 ( ) ) ;
interpreter . set ( m_dst , Value ( result , Value : : CannotFitInInt32 : : Indeed ) ) ;
return { } ;
2024-02-20 11:59:46 +01:00
}
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_double ( ) - rhs . as_double ( ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , TRY ( sub ( vm , lhs , rhs ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > BitwiseXor : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-02-20 11:59:46 +01:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto const lhs = interpreter . get ( m_lhs ) ;
auto const rhs = interpreter . get ( m_rhs ) ;
2024-02-20 11:59:46 +01:00
if ( lhs . is_int32 ( ) & & rhs . is_int32 ( ) ) {
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_i32 ( ) ^ rhs . as_i32 ( ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , TRY ( bitwise_xor ( vm , lhs , rhs ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > BitwiseAnd : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-02-20 11:59:46 +01:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto const lhs = interpreter . get ( m_lhs ) ;
auto const rhs = interpreter . get ( m_rhs ) ;
2024-02-20 11:59:46 +01:00
if ( lhs . is_int32 ( ) & & rhs . is_int32 ( ) ) {
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_i32 ( ) & rhs . as_i32 ( ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , TRY ( bitwise_and ( vm , lhs , rhs ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > BitwiseOr : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-02-20 11:59:46 +01:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto const lhs = interpreter . get ( m_lhs ) ;
auto const rhs = interpreter . get ( m_rhs ) ;
2024-02-20 11:59:46 +01:00
if ( lhs . is_int32 ( ) & & rhs . is_int32 ( ) ) {
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_i32 ( ) | rhs . as_i32 ( ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , TRY ( bitwise_or ( vm , lhs , rhs ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > UnsignedRightShift : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-02-20 11:59:46 +01:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto const lhs = interpreter . get ( m_lhs ) ;
auto const rhs = interpreter . get ( m_rhs ) ;
2024-03-04 10:56:21 +01:00
if ( lhs . is_int32 ( ) & & rhs . is_int32 ( ) ) {
2024-02-20 11:59:46 +01:00
auto const shift_count = static_cast < u32 > ( rhs . as_i32 ( ) ) % 32 ;
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( static_cast < u32 > ( lhs . as_i32 ( ) ) > > shift_count ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , TRY ( unsigned_right_shift ( vm , lhs , rhs ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > RightShift : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-02-20 11:59:46 +01:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto const lhs = interpreter . get ( m_lhs ) ;
auto const rhs = interpreter . get ( m_rhs ) ;
2024-03-04 10:56:21 +01:00
if ( lhs . is_int32 ( ) & & rhs . is_int32 ( ) ) {
2024-02-20 11:59:46 +01:00
auto const shift_count = static_cast < u32 > ( rhs . as_i32 ( ) ) % 32 ;
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_i32 ( ) > > shift_count ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , TRY ( right_shift ( vm , lhs , rhs ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > LeftShift : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-03-04 10:01:40 +01:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto const lhs = interpreter . get ( m_lhs ) ;
auto const rhs = interpreter . get ( m_rhs ) ;
2024-03-04 10:56:21 +01:00
if ( lhs . is_int32 ( ) & & rhs . is_int32 ( ) ) {
2024-03-04 10:01:40 +01:00
auto const shift_count = static_cast < u32 > ( rhs . as_i32 ( ) ) % 32 ;
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_i32 ( ) < < shift_count ) ) ;
2024-03-04 10:01:40 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , TRY ( left_shift ( vm , lhs , rhs ) ) ) ;
2024-03-04 10:01:40 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > LessThan : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-02-20 11:59:46 +01:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto const lhs = interpreter . get ( m_lhs ) ;
auto const rhs = interpreter . get ( m_rhs ) ;
2024-05-13 09:23:53 +02:00
if ( lhs . is_number ( ) & & rhs . is_number ( ) ) {
if ( lhs . is_int32 ( ) & & rhs . is_int32 ( ) ) {
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_i32 ( ) < rhs . as_i32 ( ) ) ) ;
2024-05-13 09:23:53 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_double ( ) < rhs . as_double ( ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-05-08 15:19:35 +12:00
interpreter . set ( m_dst , Value { TRY ( less_than ( vm , lhs , rhs ) ) } ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > LessThanEquals : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-02-20 11:59:46 +01:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto const lhs = interpreter . get ( m_lhs ) ;
auto const rhs = interpreter . get ( m_rhs ) ;
2024-05-13 09:23:53 +02:00
if ( lhs . is_number ( ) & & rhs . is_number ( ) ) {
if ( lhs . is_int32 ( ) & & rhs . is_int32 ( ) ) {
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_i32 ( ) < = rhs . as_i32 ( ) ) ) ;
2024-05-13 09:23:53 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_double ( ) < = rhs . as_double ( ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-05-08 15:19:35 +12:00
interpreter . set ( m_dst , Value { TRY ( less_than_equals ( vm , lhs , rhs ) ) } ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > GreaterThan : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-02-20 11:59:46 +01:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto const lhs = interpreter . get ( m_lhs ) ;
auto const rhs = interpreter . get ( m_rhs ) ;
2024-05-13 09:23:53 +02:00
if ( lhs . is_number ( ) & & rhs . is_number ( ) ) {
if ( lhs . is_int32 ( ) & & rhs . is_int32 ( ) ) {
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_i32 ( ) > rhs . as_i32 ( ) ) ) ;
2024-05-13 09:23:53 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_double ( ) > rhs . as_double ( ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-05-08 15:19:35 +12:00
interpreter . set ( m_dst , Value { TRY ( greater_than ( vm , lhs , rhs ) ) } ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > GreaterThanEquals : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-02-20 11:59:46 +01:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto const lhs = interpreter . get ( m_lhs ) ;
auto const rhs = interpreter . get ( m_rhs ) ;
2024-05-13 09:23:53 +02:00
if ( lhs . is_number ( ) & & rhs . is_number ( ) ) {
if ( lhs . is_int32 ( ) & & rhs . is_int32 ( ) ) {
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_i32 ( ) > = rhs . as_i32 ( ) ) ) ;
2024-05-13 09:23:53 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , Value ( lhs . as_double ( ) > = rhs . as_double ( ) ) ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2025-05-08 15:19:35 +12:00
interpreter . set ( m_dst , Value { TRY ( greater_than_equals ( vm , lhs , rhs ) ) } ) ;
2024-02-20 11:59:46 +01:00
return { } ;
}
2023-09-27 10:10:00 +02:00
static ThrowCompletionOr < Value > not_ ( VM & , Value value )
{
return Value ( ! value . to_boolean ( ) ) ;
}
static ThrowCompletionOr < Value > typeof_ ( VM & vm , Value value )
{
2024-07-23 10:25:06 +02:00
return value . typeof_ ( vm ) ;
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
# define JS_DEFINE_COMMON_UNARY_OP(OpTitleCase, op_snake_case) \
ThrowCompletionOr < void > OpTitleCase : : execute_impl ( Bytecode : : Interpreter & interpreter ) const \
{ \
auto & vm = interpreter . vm ( ) ; \
interpreter . set ( dst ( ) , TRY ( op_snake_case ( vm , interpreter . get ( src ( ) ) ) ) ) ; \
return { } ; \
} \
ByteString OpTitleCase : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const \
{ \
return ByteString : : formatted ( # OpTitleCase " {}, {} " , \
format_operand ( " dst " sv , dst ( ) , executable ) , \
format_operand ( " src " sv , src ( ) , executable ) ) ; \
2023-09-27 10:10:00 +02:00
}
JS_ENUMERATE_COMMON_UNARY_OPS ( JS_DEFINE_COMMON_UNARY_OP )
2025-04-29 16:08:42 +02:00
void NewArray : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto array = MUST ( Array : : create ( interpreter . realm ( ) , 0 ) ) ;
for ( size_t i = 0 ; i < m_element_count ; i + + ) {
2025-04-29 16:08:42 +02:00
array - > indexed_properties ( ) . put ( i , interpreter . get ( m_elements [ i ] ) , default_attributes ) ;
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , array ) ;
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
void NewPrimitiveArray : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-11-17 22:07:23 +02:00
{
auto array = MUST ( Array : : create ( interpreter . realm ( ) , 0 ) ) ;
2024-03-03 12:37:28 +01:00
for ( size_t i = 0 ; i < m_element_count ; i + + )
array - > indexed_properties ( ) . put ( i , m_elements [ i ] , default_attributes ) ;
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , array ) ;
2023-11-17 22:07:23 +02:00
}
2025-04-29 16:08:42 +02:00
void AddPrivateName : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-05-11 22:54:41 +00:00
{
2025-10-07 16:47:41 +02:00
auto const & name = interpreter . get_identifier ( m_name ) ;
2024-05-11 22:54:41 +00:00
interpreter . vm ( ) . running_execution_context ( ) . private_environment - > add_private_name ( name ) ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > ArrayAppend : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
return append ( interpreter . vm ( ) , interpreter . get ( dst ( ) ) , interpreter . get ( src ( ) ) , m_is_spread ) ;
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > ImportCall : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto specifier = interpreter . get ( m_specifier ) ;
auto options_value = interpreter . get ( m_options ) ;
interpreter . set ( dst ( ) , TRY ( perform_import_call ( vm , specifier , options_value ) ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > IteratorToArray : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , TRY ( iterator_to_array ( interpreter . vm ( ) , interpreter . get ( iterator ( ) ) ) ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
void NewObject : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
auto & vm = interpreter . vm ( ) ;
auto & realm = * vm . current_realm ( ) ;
interpreter . set ( dst ( ) , Object : : create ( realm , realm . intrinsics ( ) . object_prototype ( ) ) ) ;
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
void NewRegExp : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) ,
2024-02-04 08:00:54 +01:00
new_regexp (
interpreter . vm ( ) ,
interpreter . current_executable ( ) . regex_table - > get ( m_regex_index ) ,
2025-08-07 19:31:52 -04:00
interpreter . current_executable ( ) . get_string ( m_source_index ) ,
interpreter . current_executable ( ) . get_string ( m_flags_index ) ) ) ;
2024-02-04 08:00:54 +01:00
}
2025-04-29 16:08:42 +02:00
# define JS_DEFINE_NEW_BUILTIN_ERROR_OP(ErrorName) \
void New # # ErrorName : : execute_impl ( Bytecode : : Interpreter & interpreter ) const \
{ \
auto & vm = interpreter . vm ( ) ; \
auto & realm = * vm . current_realm ( ) ; \
interpreter . set ( dst ( ) , ErrorName : : create ( realm , interpreter . current_executable ( ) . get_string ( m_error_string ) ) ) ; \
} \
ByteString New # # ErrorName : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const \
{ \
return ByteString : : formatted ( " New " # ErrorName " {}, {} " , \
format_operand ( " dst " sv , m_dst , executable ) , \
executable . string_table - > get ( m_error_string ) ) ; \
2023-09-27 10:10:00 +02:00
}
JS_ENUMERATE_NEW_BUILTIN_ERROR_OPS ( JS_DEFINE_NEW_BUILTIN_ERROR_OP )
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > CopyObjectExcludingProperties : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto & realm = * vm . current_realm ( ) ;
2023-09-27 10:10:00 +02:00
2025-04-29 16:08:42 +02:00
auto from_object = interpreter . get ( m_from_object ) ;
2023-09-27 10:10:00 +02:00
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 ) {
2025-04-29 16:08:42 +02:00
excluded_names . set ( TRY ( interpreter . get ( m_excluded_names [ i ] ) . to_property_key ( vm ) ) ) ;
2023-09-27 10:10:00 +02:00
}
TRY ( to_object - > copy_data_properties ( vm , from_object , excluded_names ) ) ;
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , to_object ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > ConcatString : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto string = TRY ( interpreter . get ( src ( ) ) . to_primitive_string ( vm ) ) ;
interpreter . set ( dst ( ) , PrimitiveString : : create ( vm , interpreter . get ( dst ( ) ) . as_string ( ) , string ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-05-04 01:41:49 +02:00
enum class BindingIsKnownToBeInitialized {
No ,
Yes ,
} ;
template < BindingIsKnownToBeInitialized binding_is_known_to_be_initialized >
static ThrowCompletionOr < void > get_binding ( Interpreter & interpreter , Operand dst , IdentifierTableIndex identifier , EnvironmentCoordinate & cache )
2023-09-27 10:10:00 +02:00
{
2024-05-11 17:22:59 +02:00
auto & vm = interpreter . vm ( ) ;
2025-10-07 16:37:22 +02:00
if ( cache . is_valid ( ) ) [[likely]] {
2024-05-11 18:16:27 +02:00
auto const * environment = interpreter . running_execution_context ( ) . lexical_environment . ptr ( ) ;
2025-05-04 01:41:49 +02:00
for ( size_t i = 0 ; i < cache . hops ; + + i )
2024-05-11 17:22:59 +02:00
environment = environment - > outer_environment ( ) ;
2025-10-07 16:37:22 +02:00
if ( ! environment - > is_permanently_screwed_by_eval ( ) ) [[likely]] {
2025-05-04 01:41:49 +02:00
Value value ;
if constexpr ( binding_is_known_to_be_initialized = = BindingIsKnownToBeInitialized : : No ) {
value = TRY ( static_cast < DeclarativeEnvironment const & > ( * environment ) . get_binding_value_direct ( vm , cache . index ) ) ;
} else {
value = static_cast < DeclarativeEnvironment const & > ( * environment ) . get_initialized_binding_value_direct ( cache . index ) ;
}
interpreter . set ( dst , value ) ;
2024-05-11 17:22:59 +02:00
return { } ;
}
2025-05-04 01:41:49 +02:00
cache = { } ;
2024-05-11 17:22:59 +02:00
}
2025-10-07 16:37:22 +02:00
auto & executable = interpreter . current_executable ( ) ;
2025-05-04 01:41:49 +02:00
auto reference = TRY ( vm . resolve_binding ( executable . get_identifier ( identifier ) ) ) ;
2024-05-11 17:22:59 +02:00
if ( reference . environment_coordinate ( ) . has_value ( ) )
2025-05-04 01:41:49 +02:00
cache = reference . environment_coordinate ( ) . value ( ) ;
interpreter . set ( dst , TRY ( reference . get_value ( vm ) ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-05-04 01:41:49 +02:00
ThrowCompletionOr < void > GetBinding : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
return get_binding < BindingIsKnownToBeInitialized : : No > ( interpreter , m_dst , m_identifier , m_cache ) ;
}
ThrowCompletionOr < void > GetInitializedBinding : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
return get_binding < BindingIsKnownToBeInitialized : : Yes > ( interpreter , m_dst , m_identifier , m_cache ) ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > GetCalleeAndThisFromEnvironment : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2023-11-10 13:00:36 +01:00
auto callee_and_this = TRY ( get_callee_and_this_from_environment (
interpreter ,
2025-10-07 16:47:41 +02:00
interpreter . get_identifier ( m_identifier ) ,
2024-05-11 18:28:03 +02:00
m_cache ) ) ;
2025-04-29 16:08:42 +02:00
interpreter . set ( m_callee , callee_and_this . callee ) ;
interpreter . set ( m_this_value , callee_and_this . this_value ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > GetGlobal : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , TRY ( get_global ( interpreter , m_identifier , interpreter . current_executable ( ) . global_variable_caches [ m_cache_index ] ) ) ) ;
2023-10-20 12:56:12 +02:00
return { } ;
2023-09-27 10:10:00 +02:00
}
2025-05-01 23:58:38 +02:00
ThrowCompletionOr < void > SetGlobal : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto & binding_object = interpreter . global_object ( ) ;
auto & declarative_record = interpreter . global_declarative_environment ( ) ;
auto & cache = interpreter . current_executable ( ) . global_variable_caches [ m_cache_index ] ;
auto & shape = binding_object . shape ( ) ;
auto src = interpreter . get ( m_src ) ;
if ( cache . environment_serial_number = = declarative_record . environment_serial_number ( ) ) {
// OPTIMIZATION: For global var bindings, if the shape of the global object hasn't changed,
// we can use the cached property offset.
2025-05-06 13:16:44 +02:00
if ( & shape = = cache . entries [ 0 ] . shape ) {
auto value = binding_object . get_direct ( cache . entries [ 0 ] . property_offset . value ( ) ) ;
2025-05-01 23:58:38 +02:00
if ( value . is_accessor ( ) )
2025-05-04 16:00:13 +02:00
TRY ( call ( vm , value . as_accessor ( ) . setter ( ) , & binding_object , src ) ) ;
2025-05-01 23:58:38 +02:00
else
2025-05-06 13:16:44 +02:00
binding_object . put_direct ( cache . entries [ 0 ] . property_offset . value ( ) , src ) ;
2025-05-01 23:58:38 +02:00
return { } ;
}
// OPTIMIZATION: For global lexical bindings, if the global declarative environment hasn't changed,
// we can use the cached environment binding index.
if ( cache . has_environment_binding_index ) {
if ( cache . in_module_environment ) {
auto module = vm . running_execution_context ( ) . script_or_module . get_pointer < GC : : Ref < Module > > ( ) ;
TRY ( ( * module ) - > environment ( ) - > set_mutable_binding_direct ( vm , cache . environment_binding_index , src , vm . in_strict_mode ( ) ) ) ;
} else {
TRY ( declarative_record . set_mutable_binding_direct ( vm , cache . environment_binding_index , src , vm . in_strict_mode ( ) ) ) ;
}
return { } ;
}
}
cache . environment_serial_number = declarative_record . environment_serial_number ( ) ;
2025-10-07 16:47:41 +02:00
auto & identifier = interpreter . get_identifier ( m_identifier ) ;
2025-05-01 23:58:38 +02:00
if ( auto * module = vm . running_execution_context ( ) . script_or_module . get_pointer < GC : : Ref < 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 = * ( * module ) - > environment ( ) ;
Optional < size_t > index ;
if ( TRY ( module_environment . has_binding ( identifier , & index ) ) ) {
if ( index . has_value ( ) ) {
cache . environment_binding_index = static_cast < u32 > ( index . value ( ) ) ;
cache . has_environment_binding_index = true ;
cache . in_module_environment = true ;
return TRY ( module_environment . set_mutable_binding_direct ( vm , index . value ( ) , src , vm . in_strict_mode ( ) ) ) ;
}
return TRY ( module_environment . set_mutable_binding ( vm , identifier , src , vm . in_strict_mode ( ) ) ) ;
}
}
Optional < size_t > offset ;
if ( TRY ( declarative_record . has_binding ( identifier , & offset ) ) ) {
cache . environment_binding_index = static_cast < u32 > ( offset . value ( ) ) ;
cache . has_environment_binding_index = true ;
cache . in_module_environment = false ;
TRY ( declarative_record . set_mutable_binding ( vm , identifier , src , vm . in_strict_mode ( ) ) ) ;
return { } ;
}
if ( TRY ( binding_object . has_property ( identifier ) ) ) {
2025-09-15 17:23:39 +02:00
CacheableSetPropertyMetadata cacheable_metadata ;
2025-05-01 23:58:38 +02:00
auto success = TRY ( binding_object . internal_set ( identifier , src , & binding_object , & cacheable_metadata ) ) ;
if ( ! success & & vm . in_strict_mode ( ) ) {
// Note: Nothing like this in the spec, this is here to produce nicer errors instead of the generic one thrown by Object::set().
auto property_or_error = binding_object . internal_get_own_property ( identifier ) ;
if ( ! property_or_error . is_error ( ) ) {
auto property = property_or_error . release_value ( ) ;
if ( property . has_value ( ) & & ! property - > writable . value_or ( true ) ) {
return vm . throw_completion < TypeError > ( ErrorType : : DescWriteNonWritable , identifier ) ;
}
}
return vm . throw_completion < TypeError > ( ErrorType : : ObjectSetReturnedFalse ) ;
}
2025-09-15 17:23:39 +02:00
if ( cacheable_metadata . type = = CacheableSetPropertyMetadata : : Type : : ChangeOwnProperty ) {
2025-05-06 13:16:44 +02:00
cache . entries [ 0 ] . shape = shape ;
cache . entries [ 0 ] . property_offset = cacheable_metadata . property_offset . value ( ) ;
2025-05-01 23:58:38 +02:00
}
return { } ;
}
auto reference = TRY ( vm . resolve_binding ( identifier , & declarative_record ) ) ;
TRY ( reference . put_value ( vm , src ) ) ;
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > DeleteVariable : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-10-07 16:47:41 +02:00
auto const & string = interpreter . get_identifier ( m_identifier ) ;
2023-09-27 10:10:00 +02:00
auto reference = TRY ( vm . resolve_binding ( string ) ) ;
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , Value ( TRY ( reference . delete_ ( vm ) ) ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
void CreateLexicalEnvironment : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto make_and_swap_envs = [ & ] ( auto & old_environment ) {
2024-05-09 17:10:20 +02:00
auto declarative_environment = new_declarative_environment ( * old_environment ) . ptr ( ) ;
declarative_environment - > ensure_capacity ( m_capacity ) ;
2024-11-15 04:01:23 +13:00
GC : : Ptr < Environment > environment = declarative_environment ;
2023-09-27 10:10:00 +02:00
swap ( old_environment , environment ) ;
return environment ;
} ;
2024-05-11 18:16:27 +02:00
auto & running_execution_context = interpreter . running_execution_context ( ) ;
2024-05-01 19:33:49 +02:00
running_execution_context . saved_lexical_environments . append ( make_and_swap_envs ( running_execution_context . lexical_environment ) ) ;
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
void CreatePrivateEnvironment : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-05-11 22:54:41 +00:00
{
auto & running_execution_context = interpreter . vm ( ) . running_execution_context ( ) ;
auto outer_private_environment = running_execution_context . private_environment ;
running_execution_context . private_environment = new_private_environment ( interpreter . vm ( ) , outer_private_environment ) ;
}
2025-04-29 16:08:42 +02:00
void CreateVariableEnvironment : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-05-05 22:06:55 +02:00
{
2024-05-11 18:16:27 +02:00
auto & running_execution_context = interpreter . running_execution_context ( ) ;
2024-05-05 22:06:55 +02:00
auto var_environment = new_declarative_environment ( * running_execution_context . lexical_environment ) ;
2024-05-09 17:10:20 +02:00
var_environment - > ensure_capacity ( m_capacity ) ;
2024-05-05 22:06:55 +02:00
running_execution_context . variable_environment = var_environment ;
running_execution_context . lexical_environment = var_environment ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > EnterObjectEnvironment : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
auto object = TRY ( interpreter . get ( m_object ) . to_object ( interpreter . vm ( ) ) ) ;
2023-11-12 00:12:21 +01:00
interpreter . enter_object_environment ( * object ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
void Catch : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-11-11 23:19:46 +01:00
{
2025-04-29 16:08:42 +02:00
interpreter . catch_exception ( dst ( ) ) ;
2023-11-11 23:19:46 +01:00
}
2025-04-29 16:08:42 +02:00
void LeaveFinally : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-04-11 11:58:18 +02:00
{
interpreter . leave_finally ( ) ;
}
2025-04-29 16:08:42 +02:00
void RestoreScheduledJump : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-04-11 11:07:35 +02:00
{
interpreter . restore_scheduled_jump ( ) ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > CreateVariable : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-10-07 16:47:41 +02:00
auto const & name = interpreter . get_identifier ( m_identifier ) ;
2023-10-29 00:21:43 +02:00
return create_variable ( interpreter . vm ( ) , name , m_mode , m_is_global , m_is_immutable , m_is_strict ) ;
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
void CreateRestParams : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-05-05 22:06:55 +02:00
{
2025-04-24 01:37:30 +02:00
auto const arguments = interpreter . running_execution_context ( ) . arguments ;
2024-05-11 18:16:27 +02:00
auto arguments_count = interpreter . running_execution_context ( ) . passed_argument_count ;
2024-05-05 22:06:55 +02:00
auto array = MUST ( Array : : create ( interpreter . realm ( ) , 0 ) ) ;
for ( size_t rest_index = m_rest_index ; rest_index < arguments_count ; + + rest_index )
array - > indexed_properties ( ) . append ( arguments [ rest_index ] ) ;
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , array ) ;
2024-05-05 22:06:55 +02:00
}
2025-04-29 16:08:42 +02:00
void CreateArguments : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-05-05 22:06:55 +02:00
{
2024-05-11 18:16:27 +02:00
auto const & function = interpreter . running_execution_context ( ) . function ;
2025-04-24 01:37:30 +02:00
auto const arguments = interpreter . running_execution_context ( ) . arguments ;
2024-05-11 18:16:27 +02:00
auto const & environment = interpreter . running_execution_context ( ) . lexical_environment ;
2024-05-05 22:06:55 +02:00
2024-05-11 18:16:27 +02:00
auto passed_arguments = ReadonlySpan < Value > { arguments . data ( ) , interpreter . running_execution_context ( ) . passed_argument_count } ;
2024-05-05 22:06:55 +02:00
Object * arguments_object ;
if ( m_kind = = Kind : : Mapped ) {
arguments_object = create_mapped_arguments_object ( interpreter . vm ( ) , * function , function - > formal_parameters ( ) , passed_arguments , * environment ) ;
} else {
arguments_object = create_unmapped_arguments_object ( interpreter . vm ( ) , passed_arguments ) ;
}
2024-05-21 09:32:51 +01:00
if ( m_dst . has_value ( ) ) {
2025-04-29 16:08:42 +02:00
interpreter . set ( * m_dst , arguments_object ) ;
2025-04-04 13:48:59 +02:00
return ;
2024-05-21 09:32:51 +01:00
}
2024-05-05 22:06:55 +02:00
if ( m_is_immutable ) {
MUST ( environment - > create_immutable_binding ( interpreter . vm ( ) , interpreter . vm ( ) . names . arguments . as_string ( ) , false ) ) ;
} else {
MUST ( environment - > create_mutable_binding ( interpreter . vm ( ) , interpreter . vm ( ) . names . arguments . as_string ( ) , false ) ) ;
}
MUST ( environment - > initialize_binding ( interpreter . vm ( ) , interpreter . vm ( ) . names . arguments . as_string ( ) , arguments_object , Environment : : InitializeBindingHint : : Normal ) ) ;
}
2024-05-14 11:30:30 +02:00
template < EnvironmentMode environment_mode , BindingInitializationMode initialization_mode >
static ThrowCompletionOr < void > initialize_or_set_binding ( Interpreter & interpreter , IdentifierTableIndex identifier_index , Value value , EnvironmentCoordinate & cache )
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2024-05-13 22:03:52 +02:00
2024-05-14 11:30:30 +02:00
auto * environment = environment_mode = = EnvironmentMode : : Lexical
? interpreter . running_execution_context ( ) . lexical_environment . ptr ( )
: interpreter . running_execution_context ( ) . variable_environment . ptr ( ) ;
2025-10-07 16:37:22 +02:00
if ( cache . is_valid ( ) ) [[likely]] {
2024-05-14 11:30:30 +02:00
for ( size_t i = 0 ; i < cache . hops ; + + i )
2024-05-13 22:03:52 +02:00
environment = environment - > outer_environment ( ) ;
2025-10-07 16:37:22 +02:00
if ( ! environment - > is_permanently_screwed_by_eval ( ) ) [[likely]] {
2024-05-14 11:30:30 +02:00
if constexpr ( initialization_mode = = BindingInitializationMode : : Initialize ) {
TRY ( static_cast < DeclarativeEnvironment & > ( * environment ) . initialize_binding_direct ( vm , cache . index , value , Environment : : InitializeBindingHint : : Normal ) ) ;
} else {
TRY ( static_cast < DeclarativeEnvironment & > ( * environment ) . set_mutable_binding_direct ( vm , cache . index , value , vm . in_strict_mode ( ) ) ) ;
2024-05-13 22:03:52 +02:00
}
return { } ;
}
2024-05-14 11:30:30 +02:00
cache = { } ;
2024-05-13 22:03:52 +02:00
}
2025-10-07 16:47:41 +02:00
auto reference = TRY ( vm . resolve_binding ( interpreter . get_identifier ( identifier_index ) , environment ) ) ;
2024-05-14 11:30:30 +02:00
if ( reference . environment_coordinate ( ) . has_value ( ) )
cache = reference . environment_coordinate ( ) . value ( ) ;
if constexpr ( initialization_mode = = BindingInitializationMode : : Initialize ) {
TRY ( reference . initialize_referenced_binding ( vm , value ) ) ;
} else if ( initialization_mode = = BindingInitializationMode : : Set ) {
TRY ( reference . put_value ( vm , value ) ) ;
}
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > InitializeLexicalBinding : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-05-14 11:30:30 +02:00
{
2025-04-29 16:08:42 +02:00
return initialize_or_set_binding < EnvironmentMode : : Lexical , BindingInitializationMode : : Initialize > ( interpreter , m_identifier , interpreter . get ( m_src ) , m_cache ) ;
2024-05-14 11:30:30 +02:00
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > InitializeVariableBinding : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-05-14 11:30:30 +02:00
{
2025-04-29 16:08:42 +02:00
return initialize_or_set_binding < EnvironmentMode : : Var , BindingInitializationMode : : Initialize > ( interpreter , m_identifier , interpreter . get ( m_src ) , m_cache ) ;
2024-05-14 11:30:30 +02:00
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > SetLexicalBinding : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-05-14 11:30:30 +02:00
{
2025-04-29 16:08:42 +02:00
return initialize_or_set_binding < EnvironmentMode : : Lexical , BindingInitializationMode : : Set > ( interpreter , m_identifier , interpreter . get ( m_src ) , m_cache ) ;
2024-05-14 11:30:30 +02:00
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > SetVariableBinding : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-05-14 11:30:30 +02:00
{
2025-04-29 16:08:42 +02:00
return initialize_or_set_binding < EnvironmentMode : : Var , BindingInitializationMode : : Set > ( interpreter , m_identifier , interpreter . get ( m_src ) , m_cache ) ;
2024-05-14 11:30:30 +02:00
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > GetById : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
auto base_value = interpreter . get ( base ( ) ) ;
2023-11-05 14:33:54 +01:00
auto & cache = interpreter . current_executable ( ) . property_lookup_caches [ m_cache_index ] ;
2024-03-29 11:26:10 -04:00
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , TRY ( get_by_id ( interpreter . vm ( ) , m_base_identifier , m_property , base_value , base_value , cache , interpreter . current_executable ( ) ) ) ) ;
2023-10-18 13:26:47 +02:00
return { } ;
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > GetByIdWithThis : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
auto base_value = interpreter . get ( m_base ) ;
auto this_value = interpreter . get ( m_this_value ) ;
2023-11-05 14:33:54 +01:00
auto & cache = interpreter . current_executable ( ) . property_lookup_caches [ m_cache_index ] ;
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , TRY ( get_by_id ( interpreter . vm ( ) , { } , m_property , base_value , this_value , cache , interpreter . current_executable ( ) ) ) ) ;
2023-10-18 13:26:47 +02:00
return { } ;
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > GetLength : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-05-20 11:53:28 +02:00
{
2025-04-29 16:08:42 +02:00
auto base_value = interpreter . get ( base ( ) ) ;
2024-07-09 11:37:06 +02:00
auto & executable = interpreter . current_executable ( ) ;
auto & cache = executable . property_lookup_caches [ m_cache_index ] ;
2024-05-20 11:53:28 +02:00
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , TRY ( get_by_id < GetByIdMode : : Length > ( interpreter . vm ( ) , m_base_identifier , * executable . length_identifier , base_value , base_value , cache , executable ) ) ) ;
2024-05-20 11:53:28 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > GetLengthWithThis : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-05-20 11:53:28 +02:00
{
2025-04-29 16:08:42 +02:00
auto base_value = interpreter . get ( m_base ) ;
auto this_value = interpreter . get ( m_this_value ) ;
2024-07-09 11:37:06 +02:00
auto & executable = interpreter . current_executable ( ) ;
auto & cache = executable . property_lookup_caches [ m_cache_index ] ;
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , TRY ( get_by_id < GetByIdMode : : Length > ( interpreter . vm ( ) , { } , * executable . length_identifier , base_value , this_value , cache , executable ) ) ) ;
2024-05-20 11:53:28 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > GetPrivateById : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-10-07 16:47:41 +02:00
auto const & name = interpreter . get_identifier ( m_property ) ;
2025-04-29 16:08:42 +02:00
auto base_value = interpreter . get ( m_base ) ;
2023-09-27 10:10:00 +02:00
auto private_reference = make_private_reference ( vm , base_value , name ) ;
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , TRY ( private_reference . get_value ( vm ) ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > HasPrivateId : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto base = interpreter . get ( m_base ) ;
2024-02-04 08:00:54 +01:00
if ( ! base . is_object ( ) )
2023-09-27 10:10:00 +02:00
return vm . throw_completion < TypeError > ( ErrorType : : InOperatorWithObject ) ;
2024-05-11 18:16:27 +02:00
auto private_environment = interpreter . running_execution_context ( ) . private_environment ;
2023-09-27 10:10:00 +02:00
VERIFY ( private_environment ) ;
2025-10-07 16:47:41 +02:00
auto private_name = private_environment - > resolve_private_identifier ( interpreter . get_identifier ( m_property ) ) ;
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , Value ( base . as_object ( ) . private_element_find ( private_name ) ! = nullptr ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > PutBySpread : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-11-01 22:00:32 +01:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto value = interpreter . get ( m_src ) ;
auto base = interpreter . get ( m_base ) ;
2024-11-01 22:00:32 +01:00
// a. Let baseObj be ? ToObject(V.[[Base]]).
auto object = TRY ( base . to_object ( vm ) ) ;
TRY ( object - > copy_data_properties ( vm , value , { } ) ) ;
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > PutById : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto value = interpreter . get ( m_src ) ;
auto base = interpreter . get ( m_base ) ;
2025-10-07 16:47:41 +02:00
auto base_identifier = interpreter . get_identifier ( m_base_identifier ) ;
PropertyKey name { interpreter . get_identifier ( m_property ) , PropertyKey : : StringMayBeNumber : : No } ;
2025-09-13 16:20:41 +02:00
auto & cache = interpreter . current_executable ( ) . property_lookup_caches [ m_cache_index ] ;
TRY ( put_by_property_key ( vm , base , base , value , base_identifier , name , m_kind , & cache ) ) ;
return { } ;
}
ThrowCompletionOr < void > PutByNumericId : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
auto & vm = interpreter . vm ( ) ;
auto value = interpreter . get ( m_src ) ;
auto base = interpreter . get ( m_base ) ;
2025-10-07 16:47:41 +02:00
auto base_identifier = interpreter . get_identifier ( m_base_identifier ) ;
2025-09-13 16:20:41 +02:00
PropertyKey name { m_property_index } ;
2023-11-08 20:51:26 +01:00
auto & cache = interpreter . current_executable ( ) . property_lookup_caches [ m_cache_index ] ;
2024-03-29 12:10:52 -04:00
TRY ( put_by_property_key ( vm , base , base , value , base_identifier , name , m_kind , & cache ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > PutByIdWithThis : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto value = interpreter . get ( m_src ) ;
auto base = interpreter . get ( m_base ) ;
2025-10-07 16:47:41 +02:00
PropertyKey name { interpreter . get_identifier ( m_property ) , PropertyKey : : StringMayBeNumber : : No } ;
2023-11-08 20:51:26 +01:00
auto & cache = interpreter . current_executable ( ) . property_lookup_caches [ m_cache_index ] ;
2025-04-29 16:08:42 +02:00
TRY ( put_by_property_key ( vm , base , interpreter . get ( m_this_value ) , value , { } , name , m_kind , & cache ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > PutPrivateById : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto value = interpreter . get ( m_src ) ;
auto object = TRY ( interpreter . get ( m_base ) . to_object ( vm ) ) ;
2025-10-07 16:47:41 +02:00
auto name = interpreter . get_identifier ( m_property ) ;
2023-09-27 10:10:00 +02:00
auto private_reference = make_private_reference ( vm , object , name ) ;
TRY ( private_reference . put_value ( vm , value ) ) ;
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > DeleteById : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
auto base_value = interpreter . get ( m_base ) ;
interpreter . set ( dst ( ) , TRY ( Bytecode : : delete_by_id ( interpreter , base_value , m_property ) ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > DeleteByIdWithThis : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto base_value = interpreter . get ( m_base ) ;
2025-10-07 16:47:41 +02:00
auto const & identifier = interpreter . get_identifier ( m_property ) ;
2023-09-27 10:10:00 +02:00
bool strict = vm . in_strict_mode ( ) ;
2025-04-29 16:08:42 +02:00
auto reference = Reference { base_value , identifier , interpreter . get ( m_this_value ) , strict } ;
interpreter . set ( dst ( ) , Value ( TRY ( reference . delete_ ( vm ) ) ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > ResolveThisBinding : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & cached_this_value = interpreter . reg ( Register : : this_value ( ) ) ;
2025-04-04 23:16:34 +02:00
if ( ! cached_this_value . is_special_empty_value ( ) )
2024-05-31 20:41:29 +02:00
return { } ;
// OPTIMIZATION: Because the value of 'this' cannot be reassigned during a function execution, it's
// resolved once and then saved for subsequent use.
auto & running_execution_context = interpreter . running_execution_context ( ) ;
if ( auto function = running_execution_context . function ; function & & is < ECMAScriptFunctionObject > ( * function ) & & ! static_cast < ECMAScriptFunctionObject & > ( * function ) . allocates_function_environment ( ) ) {
2025-04-04 23:16:34 +02:00
cached_this_value = running_execution_context . this_value . value ( ) ;
2024-05-31 20:41:29 +02:00
} else {
auto & vm = interpreter . vm ( ) ;
cached_this_value = TRY ( vm . resolve_this_binding ( ) ) ;
2023-09-27 10:10:00 +02:00
}
return { } ;
}
// https://tc39.es/ecma262/#sec-makesuperpropertyreference
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > ResolveSuperBase : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
// 1. Let env be GetThisEnvironment().
2025-01-21 09:12:05 -05:00
auto & env = as < FunctionEnvironment > ( * get_this_environment ( vm ) ) ;
2023-09-27 10:10:00 +02:00
// 2. Assert: env.HasSuperBinding() is true.
VERIFY ( env . has_super_binding ( ) ) ;
// 3. Let baseValue be ? env.GetSuperBase().
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , TRY ( env . get_super_base ( ) ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
void GetNewTarget : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , interpreter . vm ( ) . get_new_target ( ) ) ;
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
void GetImportMeta : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , interpreter . vm ( ) . get_import_meta ( ) ) ;
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
static ThrowCompletionOr < Value > dispatch_builtin_call ( Bytecode : : Interpreter & interpreter , Bytecode : : Builtin builtin , ReadonlySpan < Operand > arguments )
2023-11-30 19:49:29 +01:00
{
switch ( builtin ) {
case Builtin : : MathAbs :
2025-04-29 16:08:42 +02:00
return TRY ( MathObject : : abs_impl ( interpreter . vm ( ) , interpreter . get ( arguments [ 0 ] ) ) ) ;
2023-11-30 19:49:29 +01:00
case Builtin : : MathLog :
2025-04-29 16:08:42 +02:00
return TRY ( MathObject : : log_impl ( interpreter . vm ( ) , interpreter . get ( arguments [ 0 ] ) ) ) ;
2024-02-20 15:59:45 +01:00
case Builtin : : MathPow :
2025-04-29 16:08:42 +02:00
return TRY ( MathObject : : pow_impl ( interpreter . vm ( ) , interpreter . get ( arguments [ 0 ] ) , interpreter . get ( arguments [ 1 ] ) ) ) ;
2023-11-30 19:49:29 +01:00
case Builtin : : MathExp :
2025-04-29 16:08:42 +02:00
return TRY ( MathObject : : exp_impl ( interpreter . vm ( ) , interpreter . get ( arguments [ 0 ] ) ) ) ;
2023-11-30 19:49:29 +01:00
case Builtin : : MathCeil :
2025-04-29 16:08:42 +02:00
return TRY ( MathObject : : ceil_impl ( interpreter . vm ( ) , interpreter . get ( arguments [ 0 ] ) ) ) ;
2023-11-30 19:49:29 +01:00
case Builtin : : MathFloor :
2025-04-29 16:08:42 +02:00
return TRY ( MathObject : : floor_impl ( interpreter . vm ( ) , interpreter . get ( arguments [ 0 ] ) ) ) ;
2025-04-03 11:58:30 +02:00
case Builtin : : MathImul :
2025-04-29 16:08:42 +02:00
return TRY ( MathObject : : imul_impl ( interpreter . vm ( ) , interpreter . get ( arguments [ 0 ] ) , interpreter . get ( arguments [ 1 ] ) ) ) ;
2025-04-03 12:01:31 +02:00
case Builtin : : MathRandom :
return MathObject : : random_impl ( ) ;
2023-11-30 19:49:29 +01:00
case Builtin : : MathRound :
2025-04-29 16:08:42 +02:00
return TRY ( MathObject : : round_impl ( interpreter . vm ( ) , interpreter . get ( arguments [ 0 ] ) ) ) ;
2023-11-30 19:49:29 +01:00
case Builtin : : MathSqrt :
2025-04-29 16:08:42 +02:00
return TRY ( MathObject : : sqrt_impl ( interpreter . vm ( ) , interpreter . get ( arguments [ 0 ] ) ) ) ;
2025-05-26 13:54:46 +03:00
case Builtin : : MathSin :
return TRY ( MathObject : : sin_impl ( interpreter . vm ( ) , interpreter . get ( arguments [ 0 ] ) ) ) ;
2025-05-26 14:03:37 +03:00
case Builtin : : MathCos :
return TRY ( MathObject : : cos_impl ( interpreter . vm ( ) , interpreter . get ( arguments [ 0 ] ) ) ) ;
2025-05-26 14:07:59 +03:00
case Builtin : : MathTan :
return TRY ( MathObject : : tan_impl ( interpreter . vm ( ) , interpreter . get ( arguments [ 0 ] ) ) ) ;
2025-06-01 18:44:18 +02:00
case Builtin : : ArrayIteratorPrototypeNext :
case Builtin : : MapIteratorPrototypeNext :
case Builtin : : SetIteratorPrototypeNext :
case Builtin : : StringIteratorPrototypeNext :
VERIFY_NOT_REACHED ( ) ;
2023-11-30 19:49:29 +01:00
case Bytecode : : Builtin : : __Count :
VERIFY_NOT_REACHED ( ) ;
}
VERIFY_NOT_REACHED ( ) ;
}
2025-08-30 10:36:32 +02:00
template < CallType call_type >
static ThrowCompletionOr < void > execute_call (
Bytecode : : Interpreter & interpreter ,
Value callee ,
Value this_value ,
ReadonlySpan < Operand > arguments ,
Operand dst ,
Optional < StringTableIndex > const & expression_string )
2023-09-27 10:10:00 +02:00
{
2025-08-30 10:36:32 +02:00
TRY ( throw_if_needed_for_call ( interpreter , callee , call_type , expression_string ) ) ;
2025-04-27 11:53:11 +02:00
auto & function = callee . as_function ( ) ;
ExecutionContext * callee_context = nullptr ;
size_t registers_and_constants_and_locals_count = 0 ;
2025-08-30 10:36:32 +02:00
size_t argument_count = arguments . size ( ) ;
2025-04-27 11:53:11 +02:00
TRY ( function . get_stack_frame_size ( registers_and_constants_and_locals_count , argument_count ) ) ;
2025-08-30 10:36:32 +02:00
ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK_WITHOUT_CLEARING_ARGS ( callee_context , registers_and_constants_and_locals_count , max ( arguments . size ( ) , argument_count ) ) ;
2025-04-27 11:53:11 +02:00
auto * callee_context_argument_values = callee_context - > arguments . data ( ) ;
auto const callee_context_argument_count = callee_context - > arguments . size ( ) ;
2025-08-30 10:36:32 +02:00
auto const insn_argument_count = arguments . size ( ) ;
2025-04-27 11:53:11 +02:00
for ( size_t i = 0 ; i < insn_argument_count ; + + i )
2025-08-30 10:36:32 +02:00
callee_context_argument_values [ i ] = interpreter . get ( arguments [ i ] ) ;
2025-04-27 11:53:11 +02:00
for ( size_t i = insn_argument_count ; i < callee_context_argument_count ; + + i )
callee_context_argument_values [ i ] = js_undefined ( ) ;
callee_context - > passed_argument_count = insn_argument_count ;
2024-10-31 22:47:30 +01:00
2025-08-30 10:36:32 +02:00
Value retval ;
if ( call_type = = CallType : : DirectEval & & callee = = interpreter . realm ( ) . intrinsics ( ) . eval_function ( ) ) {
retval = TRY ( perform_eval ( interpreter . vm ( ) , ! callee_context - > arguments . is_empty ( ) ? callee_context - > arguments [ 0 ] : js_undefined ( ) , interpreter . vm ( ) . in_strict_mode ( ) ? CallerMode : : Strict : CallerMode : : NonStrict , EvalMode : : Direct ) ) ;
} else if ( call_type = = CallType : : Construct ) {
retval = TRY ( function . internal_construct ( * callee_context , function ) ) ;
} else {
retval = TRY ( function . internal_call ( * callee_context , this_value ) ) ;
}
interpreter . set ( dst , retval ) ;
2024-10-31 22:47:30 +01:00
return { } ;
}
2025-08-30 10:36:32 +02:00
ThrowCompletionOr < void > Call : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-10-31 22:47:30 +01:00
{
2025-08-30 10:36:32 +02:00
return execute_call < CallType : : Call > ( interpreter , interpreter . get ( m_callee ) , interpreter . get ( m_this_value ) , { m_arguments , m_argument_count } , m_dst , m_expression_string ) ;
}
2024-10-31 22:47:30 +01:00
2025-08-30 10:36:32 +02:00
NEVER_INLINE ThrowCompletionOr < void > CallConstruct : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
return execute_call < CallType : : Construct > ( interpreter , interpreter . get ( m_callee ) , js_undefined ( ) , { m_arguments , m_argument_count } , m_dst , m_expression_string ) ;
2024-10-31 22:47:30 +01:00
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > CallDirectEval : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-10-31 22:47:30 +01:00
{
2025-08-30 10:36:32 +02:00
return execute_call < CallType : : DirectEval > ( interpreter , interpreter . get ( m_callee ) , interpreter . get ( m_this_value ) , { m_arguments , m_argument_count } , m_dst , m_expression_string ) ;
2024-10-31 22:47:30 +01:00
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > CallBuiltin : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-10-31 22:47:30 +01:00
{
2025-04-29 16:08:42 +02:00
auto callee = interpreter . get ( m_callee ) ;
2024-10-31 22:47:30 +01:00
if ( m_argument_count = = Bytecode : : builtin_argument_count ( m_builtin ) & & callee . is_object ( ) & & interpreter . realm ( ) . get_builtin_value ( m_builtin ) = = & callee . as_object ( ) ) {
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , TRY ( dispatch_builtin_call ( interpreter , m_builtin , { m_arguments , m_argument_count } ) ) ) ;
2023-11-30 19:49:29 +01:00
return { } ;
}
2025-08-30 10:36:32 +02:00
return execute_call < CallType : : Call > ( interpreter , callee , interpreter . get ( m_this_value ) , { m_arguments , m_argument_count } , m_dst , m_expression_string ) ;
2023-09-27 10:10:00 +02:00
}
2025-08-30 11:00:54 +02:00
template < CallType call_type >
static ThrowCompletionOr < void > call_with_argument_array (
Bytecode : : Interpreter & interpreter ,
Value callee ,
Value this_value ,
Value arguments ,
Operand dst ,
Optional < StringTableIndex > const & expression_string )
2023-09-27 10:10:00 +02:00
{
2025-08-30 11:00:54 +02:00
TRY ( throw_if_needed_for_call ( interpreter , callee , call_type , expression_string ) ) ;
auto & function = callee . as_function ( ) ;
auto & argument_array = arguments . as_array ( ) ;
auto argument_array_length = argument_array . indexed_properties ( ) . array_like_size ( ) ;
ExecutionContext * callee_context = nullptr ;
size_t argument_count = argument_array_length ;
size_t registers_and_constants_and_locals_count = 0 ;
TRY ( function . get_stack_frame_size ( registers_and_constants_and_locals_count , argument_count ) ) ;
ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK_WITHOUT_CLEARING_ARGS ( callee_context , registers_and_constants_and_locals_count , max ( argument_array_length , argument_count ) ) ;
auto * callee_context_argument_values = callee_context - > arguments . data ( ) ;
auto const callee_context_argument_count = callee_context - > arguments . size ( ) ;
auto const insn_argument_count = argument_array_length ;
for ( size_t i = 0 ; i < insn_argument_count ; + + i ) {
if ( auto maybe_value = argument_array . indexed_properties ( ) . get ( i ) ; maybe_value . has_value ( ) )
callee_context_argument_values [ i ] = maybe_value . release_value ( ) . value ;
else
callee_context_argument_values [ i ] = js_undefined ( ) ;
}
for ( size_t i = insn_argument_count ; i < callee_context_argument_count ; + + i )
callee_context_argument_values [ i ] = js_undefined ( ) ;
callee_context - > passed_argument_count = insn_argument_count ;
Value retval ;
if ( call_type = = CallType : : DirectEval & & callee = = interpreter . realm ( ) . intrinsics ( ) . eval_function ( ) ) {
auto & vm = interpreter . vm ( ) ;
retval = TRY ( perform_eval ( vm , ! callee_context - > arguments . is_empty ( ) ? callee_context - > arguments [ 0 ] : js_undefined ( ) , vm . in_strict_mode ( ) ? CallerMode : : Strict : CallerMode : : NonStrict , EvalMode : : Direct ) ) ;
} else if ( call_type = = CallType : : Construct ) {
retval = TRY ( function . internal_construct ( * callee_context , function ) ) ;
} else {
retval = TRY ( function . internal_call ( * callee_context , this_value ) ) ;
}
interpreter . set ( dst , retval ) ;
2023-10-20 13:20:28 +02:00
return { } ;
2023-09-27 10:10:00 +02:00
}
2025-08-30 11:00:54 +02:00
ThrowCompletionOr < void > CallWithArgumentArray : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
return call_with_argument_array < CallType : : Call > ( interpreter , interpreter . get ( callee ( ) ) , interpreter . get ( this_value ( ) ) , interpreter . get ( arguments ( ) ) , dst ( ) , expression_string ( ) ) ;
}
ThrowCompletionOr < void > CallDirectEvalWithArgumentArray : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
return call_with_argument_array < CallType : : DirectEval > ( interpreter , interpreter . get ( callee ( ) ) , interpreter . get ( this_value ( ) ) , interpreter . get ( arguments ( ) ) , dst ( ) , expression_string ( ) ) ;
}
ThrowCompletionOr < void > CallConstructWithArgumentArray : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
{
return call_with_argument_array < CallType : : Construct > ( interpreter , interpreter . get ( callee ( ) ) , js_undefined ( ) , interpreter . get ( arguments ( ) ) , dst ( ) , expression_string ( ) ) ;
}
2023-09-27 10:10:00 +02:00
// 13.3.7.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > SuperCallWithArgumentArray : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-08-30 16:26:25 +02:00
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 ) ;
// NON-STANDARD: We're doing this step earlier to streamline control flow.
// 5. If IsConstructor(func) is false, throw a TypeError exception.
if ( ! Value ( func ) . is_constructor ( ) )
return vm . throw_completion < TypeError > ( ErrorType : : NotAConstructor , " Super constructor " ) ;
auto & function = static_cast < FunctionObject & > ( * func ) ;
// 4. Let argList be ? ArgumentListEvaluation of Arguments.
auto & argument_array = interpreter . get ( m_arguments ) . as_array ( ) ;
size_t argument_array_length = 0 ;
if ( m_is_synthetic ) {
argument_array_length = MUST ( length_of_array_like ( vm , argument_array ) ) ;
} else {
argument_array_length = argument_array . indexed_properties ( ) . array_like_size ( ) ;
}
ExecutionContext * callee_context = nullptr ;
size_t argument_count = argument_array_length ;
size_t registers_and_constants_and_locals_count = 0 ;
TRY ( function . get_stack_frame_size ( registers_and_constants_and_locals_count , argument_count ) ) ;
ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK_WITHOUT_CLEARING_ARGS ( callee_context , registers_and_constants_and_locals_count , max ( argument_array_length , argument_count ) ) ;
auto * callee_context_argument_values = callee_context - > arguments . data ( ) ;
auto const callee_context_argument_count = callee_context - > arguments . size ( ) ;
auto const insn_argument_count = argument_array_length ;
if ( m_is_synthetic ) {
for ( size_t i = 0 ; i < insn_argument_count ; + + i )
callee_context_argument_values [ i ] = argument_array . get_without_side_effects ( PropertyKey { i } ) ;
} else {
for ( size_t i = 0 ; i < insn_argument_count ; + + i ) {
if ( auto maybe_value = argument_array . indexed_properties ( ) . get ( i ) ; maybe_value . has_value ( ) )
callee_context_argument_values [ i ] = maybe_value . release_value ( ) . value ;
else
callee_context_argument_values [ i ] = js_undefined ( ) ;
}
}
for ( size_t i = insn_argument_count ; i < callee_context_argument_count ; + + i )
callee_context_argument_values [ i ] = js_undefined ( ) ;
callee_context - > passed_argument_count = insn_argument_count ;
// 6. Let result be ? Construct(func, argList, newTarget).
auto result = TRY ( function . internal_construct ( * callee_context , new_target . as_function ( ) ) ) ;
// 7. Let thisER be GetThisEnvironment().
auto & this_environment = as < 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 . set ( m_dst , result ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
void NewFunction : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
auto & vm = interpreter . vm ( ) ;
interpreter . set ( dst ( ) , new_function ( vm , m_function_node , m_lhs_name , m_home_object ) ) ;
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
void Return : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-05-16 12:28:51 +12:00
interpreter . do_return ( interpreter . get ( m_value ) ) ;
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > Increment : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto old_value = interpreter . get ( dst ( ) ) ;
2024-01-27 20:51:11 +01:00
// OPTIMIZATION: Fast path for Int32 values.
if ( old_value . is_int32 ( ) ) {
auto integer_value = old_value . as_i32 ( ) ;
if ( integer_value ! = NumericLimits < i32 > : : max ( ) ) [[likely]] {
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , Value { integer_value + 1 } ) ;
2024-01-27 20:51:11 +01:00
return { } ;
}
}
old_value = TRY ( old_value . to_numeric ( vm ) ) ;
2023-09-27 10:10:00 +02:00
if ( old_value . is_number ( ) )
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , Value ( old_value . as_double ( ) + 1 ) ) ;
2023-09-27 10:10:00 +02:00
else
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , BigInt : : create ( vm , old_value . as_bigint ( ) . big_integer ( ) . plus ( Crypto : : SignedBigInteger { 1 } ) ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > PostfixIncrement : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-02-20 11:45:01 +01:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto old_value = interpreter . get ( m_src ) ;
2024-02-20 11:45:01 +01:00
// OPTIMIZATION: Fast path for Int32 values.
if ( old_value . is_int32 ( ) ) {
auto integer_value = old_value . as_i32 ( ) ;
if ( integer_value ! = NumericLimits < i32 > : : max ( ) ) [[likely]] {
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , old_value ) ;
interpreter . set ( m_src , Value { integer_value + 1 } ) ;
2024-02-20 11:45:01 +01:00
return { } ;
}
}
old_value = TRY ( old_value . to_numeric ( vm ) ) ;
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , old_value ) ;
2024-02-20 11:45:01 +01:00
if ( old_value . is_number ( ) )
2025-04-29 16:08:42 +02:00
interpreter . set ( m_src , Value ( old_value . as_double ( ) + 1 ) ) ;
2024-02-20 11:45:01 +01:00
else
2025-04-29 16:08:42 +02:00
interpreter . set ( m_src , BigInt : : create ( vm , old_value . as_bigint ( ) . big_integer ( ) . plus ( Crypto : : SignedBigInteger { 1 } ) ) ) ;
2024-02-20 11:45:01 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > Decrement : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto old_value = interpreter . get ( dst ( ) ) ;
2024-02-04 08:00:54 +01:00
old_value = TRY ( old_value . to_numeric ( vm ) ) ;
2023-09-27 10:10:00 +02:00
if ( old_value . is_number ( ) )
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , Value ( old_value . as_double ( ) - 1 ) ) ;
2023-09-27 10:10:00 +02:00
else
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , BigInt : : create ( vm , old_value . as_bigint ( ) . big_integer ( ) . minus ( Crypto : : SignedBigInteger { 1 } ) ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > PostfixDecrement : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-02-20 11:45:01 +01:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto old_value = interpreter . get ( m_src ) ;
2024-02-20 11:45:01 +01:00
old_value = TRY ( old_value . to_numeric ( vm ) ) ;
2025-04-29 16:08:42 +02:00
interpreter . set ( m_dst , old_value ) ;
2024-02-20 11:45:01 +01:00
if ( old_value . is_number ( ) )
2025-04-29 16:08:42 +02:00
interpreter . set ( m_src , Value ( old_value . as_double ( ) - 1 ) ) ;
2024-02-20 11:45:01 +01:00
else
2025-04-29 16:08:42 +02:00
interpreter . set ( m_src , BigInt : : create ( vm , old_value . as_bigint ( ) . big_integer ( ) . minus ( Crypto : : SignedBigInteger { 1 } ) ) ) ;
2024-02-20 11:45:01 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > Throw : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
return throw_completion ( interpreter . get ( src ( ) ) ) ;
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > ThrowIfNotObject : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto src = interpreter . get ( m_src ) ;
2024-02-04 08:00:54 +01:00
if ( ! src . is_object ( ) )
return vm . throw_completion < TypeError > ( ErrorType : : NotAnObject , src . to_string_without_side_effects ( ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > ThrowIfNullish : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto value = interpreter . get ( m_src ) ;
2023-09-27 10:10:00 +02:00
if ( value . is_nullish ( ) )
return vm . throw_completion < TypeError > ( ErrorType : : NotObjectCoercible , value . to_string_without_side_effects ( ) ) ;
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > ThrowIfTDZ : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-02-04 08:00:54 +01:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto value = interpreter . get ( m_src ) ;
2025-04-04 23:16:34 +02:00
if ( value . is_special_empty_value ( ) )
2024-02-04 08:00:54 +01:00
return vm . throw_completion < ReferenceError > ( ErrorType : : BindingNotInitialized , value . to_string_without_side_effects ( ) ) ;
return { } ;
}
2025-04-29 16:08:42 +02:00
void LeaveLexicalEnvironment : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2024-05-11 18:16:27 +02:00
auto & running_execution_context = interpreter . running_execution_context ( ) ;
2024-05-01 19:33:49 +02:00
running_execution_context . lexical_environment = running_execution_context . saved_lexical_environments . take_last ( ) ;
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
void LeavePrivateEnvironment : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-05-11 22:54:41 +00:00
{
auto & running_execution_context = interpreter . vm ( ) . running_execution_context ( ) ;
running_execution_context . private_environment = running_execution_context . private_environment - > outer_environment ( ) ;
}
2025-04-29 16:08:42 +02:00
void LeaveUnwindContext : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
interpreter . leave_unwind_context ( ) ;
}
2025-04-29 16:08:42 +02:00
void Yield : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
auto yielded_value = interpreter . get ( m_value ) . is_special_empty_value ( ) ? js_undefined ( ) : interpreter . get ( m_value ) ;
2024-05-18 17:25:43 +02:00
interpreter . do_return (
interpreter . do_yield ( yielded_value , m_continuation_label ) ) ;
}
2024-02-04 08:00:54 +01:00
2025-04-29 16:08:42 +02:00
void PrepareYield : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2024-05-18 17:25:43 +02:00
{
2025-04-29 16:08:42 +02:00
auto value = interpreter . get ( m_value ) . is_special_empty_value ( ) ? js_undefined ( ) : interpreter . get ( m_value ) ;
interpreter . set ( m_dest , interpreter . do_yield ( value , { } ) ) ;
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
void Await : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
auto yielded_value = interpreter . get ( m_argument ) . is_special_empty_value ( ) ? js_undefined ( ) : interpreter . get ( m_argument ) ;
2023-09-27 10:10:00 +02:00
// FIXME: If we get a pointer, which is not accurately representable as a double
// will cause this to explode
2025-03-31 09:32:39 +01:00
auto continuation_value = Value ( m_continuation_label . address ( ) ) ;
auto result = interpreter . vm ( ) . heap ( ) . allocate < GeneratorResult > ( yielded_value , continuation_value , true ) ;
interpreter . do_return ( result ) ;
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > GetByValue : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , TRY ( get_by_value ( interpreter . vm ( ) , m_base_identifier , interpreter . get ( m_base ) , interpreter . get ( m_property ) , interpreter . current_executable ( ) ) ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > GetByValueWithThis : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto property_key_value = interpreter . get ( m_property ) ;
auto object = TRY ( interpreter . get ( m_base ) . to_object ( vm ) ) ;
2023-09-27 10:10:00 +02:00
auto property_key = TRY ( property_key_value . to_property_key ( vm ) ) ;
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , TRY ( object - > internal_get ( property_key , interpreter . get ( m_this_value ) ) ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > PutByValue : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto value = interpreter . get ( m_src ) ;
2025-10-07 16:47:41 +02:00
auto base_identifier = interpreter . get_identifier ( m_base_identifier ) ;
2025-04-29 16:08:42 +02:00
TRY ( put_by_value ( vm , interpreter . get ( m_base ) , base_identifier , interpreter . get ( m_property ) , value , m_kind ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > PutByValueWithThis : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto value = interpreter . get ( m_src ) ;
auto base = interpreter . get ( m_base ) ;
auto property_key = TRY ( interpreter . get ( m_property ) . to_property_key ( vm ) ) ;
TRY ( put_by_property_key ( vm , base , interpreter . get ( m_this_value ) , value , { } , property_key , m_kind ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > DeleteByValue : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
auto base_value = interpreter . get ( m_base ) ;
auto property_key_value = interpreter . get ( m_property ) ;
interpreter . set ( dst ( ) , TRY ( delete_by_value ( interpreter , base_value , property_key_value ) ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > DeleteByValueWithThis : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-04-29 16:08:42 +02:00
auto property_key_value = interpreter . get ( m_property ) ;
auto base_value = interpreter . get ( m_base ) ;
auto this_value = interpreter . get ( m_this_value ) ;
interpreter . set ( dst ( ) , TRY ( delete_by_value_with_this ( interpreter , base_value , property_key_value , this_value ) ) ) ;
2023-10-29 21:37:52 +01:00
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > GetIterator : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , TRY ( get_iterator ( vm , interpreter . get ( iterable ( ) ) , m_hint ) ) ) ;
2023-12-07 10:44:41 +01:00
return { } ;
}
2025-04-29 16:08:42 +02:00
void GetObjectFromIteratorRecord : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-12-07 10:44:41 +01:00
{
2025-04-29 16:08:42 +02:00
auto & iterator_record = static_cast < IteratorRecord & > ( interpreter . get ( m_iterator_record ) . as_cell ( ) ) ;
interpreter . set ( m_object , iterator_record . iterator ) ;
2023-12-07 10:44:41 +01:00
}
2025-04-29 16:08:42 +02:00
void GetNextMethodFromIteratorRecord : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-12-07 10:44:41 +01:00
{
2025-04-29 16:08:42 +02:00
auto & iterator_record = static_cast < IteratorRecord & > ( interpreter . get ( m_iterator_record ) . as_cell ( ) ) ;
interpreter . set ( m_next_method , iterator_record . next_method ) ;
2023-09-27 10:10:00 +02:00
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > GetMethod : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-10-07 16:47:41 +02:00
auto identifier = interpreter . get_identifier ( m_property ) ;
2025-04-29 16:08:42 +02:00
auto method = TRY ( interpreter . get ( m_object ) . get_method ( vm , identifier ) ) ;
interpreter . set ( dst ( ) , method ? : js_undefined ( ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > GetObjectPropertyIterator : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2025-05-03 10:17:11 +02:00
auto iterator_record = TRY ( get_object_property_iterator ( interpreter , interpreter . get ( object ( ) ) ) ) ;
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , iterator_record ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > IteratorClose : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto & iterator = static_cast < IteratorRecord & > ( interpreter . get ( m_iterator_record ) . as_cell ( ) ) ;
2023-09-27 10:10:00 +02:00
// FIXME: Return the value of the resulting completion. (Note that m_completion_value can be empty!)
2025-04-04 18:11:45 +02:00
TRY ( iterator_close ( vm , iterator , Completion { m_completion_type , m_completion_value . value_or ( js_undefined ( ) ) } ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > AsyncIteratorClose : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto & iterator = static_cast < IteratorRecord & > ( interpreter . get ( m_iterator_record ) . as_cell ( ) ) ;
2023-09-27 10:10:00 +02:00
// FIXME: Return the value of the resulting completion. (Note that m_completion_value can be empty!)
2025-04-04 18:11:45 +02:00
TRY ( async_iterator_close ( vm , iterator , Completion { m_completion_type , m_completion_value . value_or ( js_undefined ( ) ) } ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > IteratorNext : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2025-04-29 16:08:42 +02:00
auto & iterator_record = static_cast < IteratorRecord & > ( interpreter . get ( m_iterator_record ) . as_cell ( ) ) ;
interpreter . set ( dst ( ) , TRY ( iterator_next ( vm , iterator_record ) ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-05-01 16:05:24 +03:00
ThrowCompletionOr < void > IteratorNextUnpack : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2025-04-30 16:31:26 +03:00
{
auto & vm = interpreter . vm ( ) ;
auto & iterator_record = static_cast < IteratorRecord & > ( interpreter . get ( m_iterator_record ) . as_cell ( ) ) ;
2025-05-12 17:32:54 +03:00
auto iteration_result_or_done = TRY ( iterator_step ( vm , iterator_record ) ) ;
if ( iteration_result_or_done . has < IterationDone > ( ) ) {
interpreter . set ( dst_done ( ) , Value ( true ) ) ;
return { } ;
2025-04-30 16:31:26 +03:00
}
2025-05-12 17:32:54 +03:00
auto & iteration_result = iteration_result_or_done . get < IterationResult > ( ) ;
interpreter . set ( dst_done ( ) , TRY ( iteration_result . done ) ) ;
interpreter . set ( dst_value ( ) , TRY ( iteration_result . value ) ) ;
2025-04-30 16:31:26 +03:00
return { } ;
}
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > NewClass : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
Value super_class ;
if ( m_super_class . has_value ( ) )
2025-04-29 16:08:42 +02:00
super_class = interpreter . get ( m_super_class . value ( ) ) ;
2024-05-11 22:54:41 +00:00
Vector < Value > element_keys ;
for ( size_t i = 0 ; i < m_element_keys_count ; + + i ) {
Value element_key ;
if ( m_element_keys [ i ] . has_value ( ) )
2025-04-29 16:08:42 +02:00
element_key = interpreter . get ( m_element_keys [ i ] . value ( ) ) ;
2024-05-11 22:54:41 +00:00
element_keys . append ( element_key ) ;
}
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , TRY ( new_class ( interpreter . vm ( ) , super_class , m_class_expression , m_lhs_name , element_keys ) ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
// 13.5.3.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-typeof-operator-runtime-semantics-evaluation
2025-04-29 16:08:42 +02:00
ThrowCompletionOr < void > TypeofBinding : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2024-06-14 13:57:51 +02:00
2025-10-07 16:37:22 +02:00
if ( m_cache . is_valid ( ) ) [[likely]] {
2024-06-14 13:57:51 +02:00
auto const * environment = interpreter . running_execution_context ( ) . lexical_environment . ptr ( ) ;
for ( size_t i = 0 ; i < m_cache . hops ; + + i )
environment = environment - > outer_environment ( ) ;
2025-10-07 16:37:22 +02:00
if ( ! environment - > is_permanently_screwed_by_eval ( ) ) [[likely]] {
2024-06-14 13:57:51 +02:00
auto value = TRY ( static_cast < DeclarativeEnvironment const & > ( * environment ) . get_binding_value_direct ( vm , m_cache . index ) ) ;
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , value . typeof_ ( vm ) ) ;
2024-06-14 13:57:51 +02:00
return { } ;
}
m_cache = { } ;
}
// 1. Let val be the result of evaluating UnaryExpression.
2025-10-07 16:47:41 +02:00
auto reference = TRY ( vm . resolve_binding ( interpreter . get_identifier ( m_identifier ) ) ) ;
2024-06-14 13:57:51 +02:00
// 2. If val is a Reference Record, then
// a. If IsUnresolvableReference(val) is true, return "undefined".
if ( reference . is_unresolvable ( ) ) {
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , PrimitiveString : : create ( vm , " undefined " _string ) ) ;
2024-06-14 13:57:51 +02:00
return { } ;
}
// 3. Set val to ? GetValue(val).
auto value = TRY ( reference . get_value ( vm ) ) ;
if ( reference . environment_coordinate ( ) . has_value ( ) )
m_cache = reference . environment_coordinate ( ) . value ( ) ;
// 4. NOTE: This step is replaced in section B.3.6.3.
// 5. Return a String according to Table 41.
2025-04-29 16:08:42 +02:00
interpreter . set ( dst ( ) , value . typeof_ ( vm ) ) ;
2023-09-27 10:10:00 +02:00
return { } ;
}
2025-04-29 16:08:42 +02:00
void BlockDeclarationInstantiation : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2023-09-27 10:10:00 +02:00
{
auto & vm = interpreter . vm ( ) ;
2024-05-11 18:16:27 +02:00
auto old_environment = interpreter . running_execution_context ( ) . lexical_environment ;
auto & running_execution_context = interpreter . running_execution_context ( ) ;
2024-05-01 19:33:49 +02:00
running_execution_context . saved_lexical_environments . append ( old_environment ) ;
running_execution_context . lexical_environment = new_declarative_environment ( * old_environment ) ;
m_scope_node . block_declaration_instantiation ( vm , running_execution_context . lexical_environment ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString Mov : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " Mov {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " src " sv , m_src , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString NewArray : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
StringBuilder builder ;
2024-02-04 08:00:54 +01:00
builder . appendff ( " NewArray {} " , format_operand ( " dst " sv , dst ( ) , executable ) ) ;
2023-09-27 10:10:00 +02:00
if ( m_element_count ! = 0 ) {
2024-05-08 12:43:08 +02:00
builder . appendff ( " , {} " , format_operand_list ( " args " sv , { m_elements , m_element_count } , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2023-12-16 17:49:34 +03:30
return builder . to_byte_string ( ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString NewPrimitiveArray : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-11-17 22:07:23 +02:00
{
2025-04-08 13:50:50 -04:00
return ByteString : : formatted ( " NewPrimitiveArray {}, {} " ,
2024-02-04 08:00:54 +01:00
format_operand ( " dst " sv , dst ( ) , executable ) ,
2024-03-03 12:37:28 +01:00
format_value_list ( " elements " sv , elements ( ) ) ) ;
2023-11-17 22:07:23 +02:00
}
2024-05-11 22:54:41 +00:00
ByteString AddPrivateName : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
2025-04-08 13:50:50 -04:00
return ByteString : : formatted ( " AddPrivateName {} " , executable . identifier_table - > get ( m_name ) ) ;
2024-05-11 22:54:41 +00:00
}
2024-02-04 08:00:54 +01:00
ByteString ArrayAppend : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " Append {}, {}{} " ,
format_operand ( " dst " sv , dst ( ) , executable ) ,
format_operand ( " src " sv , src ( ) , executable ) ,
m_is_spread ? " ** " sv : " " sv ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString IteratorToArray : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " IteratorToArray {}, {} " ,
format_operand ( " dst " sv , dst ( ) , executable ) ,
format_operand ( " iterator " sv , iterator ( ) , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString NewObject : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " NewObject {} " , format_operand ( " dst " sv , dst ( ) , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2023-12-16 17:49:34 +03:30
ByteString NewRegExp : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2025-04-05 21:46:37 +02:00
return ByteString : : formatted ( " NewRegExp {}, source: \" {} \" flags: \" {} \" " ,
2024-02-04 08:00:54 +01:00
format_operand ( " dst " sv , dst ( ) , executable ) ,
2025-04-05 21:46:37 +02:00
executable . get_string ( m_source_index ) ,
executable . get_string ( m_flags_index ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString CopyObjectExcludingProperties : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
StringBuilder builder ;
2024-02-04 08:00:54 +01:00
builder . appendff ( " CopyObjectExcludingProperties {}, {} " ,
format_operand ( " dst " sv , dst ( ) , executable ) ,
format_operand ( " from " sv , m_from_object , executable ) ) ;
2023-09-27 10:10:00 +02:00
if ( m_excluded_names_count ! = 0 ) {
builder . append ( " excluding:[ " sv ) ;
2024-02-04 08:00:54 +01:00
for ( size_t i = 0 ; i < m_excluded_names_count ; + + i ) {
if ( i ! = 0 )
builder . append ( " , " sv ) ;
builder . append ( format_operand ( " # " sv , m_excluded_names [ i ] , executable ) ) ;
}
2023-09-27 10:10:00 +02:00
builder . append ( ' ] ' ) ;
}
2023-12-16 17:49:34 +03:30
return builder . to_byte_string ( ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString ConcatString : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " ConcatString {}, {} " ,
format_operand ( " dst " sv , dst ( ) , executable ) ,
format_operand ( " src " sv , src ( ) , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2023-12-16 17:49:34 +03:30
ByteString GetCalleeAndThisFromEnvironment : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " GetCalleeAndThisFromEnvironment {}, {} <- {} " ,
format_operand ( " callee " sv , m_callee , executable ) ,
format_operand ( " this " sv , m_this_value , executable ) ,
executable . identifier_table - > get ( m_identifier ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-05-14 11:32:04 +02:00
ByteString GetBinding : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-05-14 11:32:04 +02:00
return ByteString : : formatted ( " GetBinding {}, {} " ,
2024-02-04 08:00:54 +01:00
format_operand ( " dst " sv , dst ( ) , executable ) ,
executable . identifier_table - > get ( m_identifier ) ) ;
2023-09-27 10:10:00 +02:00
}
2025-05-04 01:41:49 +02:00
ByteString GetInitializedBinding : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
return ByteString : : formatted ( " GetInitializedBinding {}, {} " ,
format_operand ( " dst " sv , dst ( ) , executable ) ,
executable . identifier_table - > get ( m_identifier ) ) ;
}
2023-12-16 17:49:34 +03:30
ByteString GetGlobal : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " GetGlobal {}, {} " , format_operand ( " dst " sv , dst ( ) , executable ) ,
executable . identifier_table - > get ( m_identifier ) ) ;
2023-09-27 10:10:00 +02:00
}
2025-05-01 23:58:38 +02:00
ByteString SetGlobal : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
return ByteString : : formatted ( " SetGlobal {}, {} " ,
executable . identifier_table - > get ( m_identifier ) ,
format_operand ( " src " sv , src ( ) , executable ) ) ;
}
2023-12-16 17:49:34 +03:30
ByteString DeleteVariable : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-05-06 10:12:02 +02:00
return ByteString : : formatted ( " DeleteVariable {} " , executable . identifier_table - > get ( m_identifier ) ) ;
2023-09-27 10:10:00 +02:00
}
2023-12-16 17:49:34 +03:30
ByteString CreateLexicalEnvironment : : to_byte_string_impl ( Bytecode : : Executable const & ) const
2023-09-27 10:10:00 +02:00
{
return " CreateLexicalEnvironment " sv ;
}
2024-05-11 22:54:41 +00:00
ByteString CreatePrivateEnvironment : : to_byte_string_impl ( Bytecode : : Executable const & ) const
{
return " CreatePrivateEnvironment " sv ;
}
2024-05-05 22:06:55 +02:00
ByteString CreateVariableEnvironment : : to_byte_string_impl ( Bytecode : : Executable const & ) const
{
return " CreateVariableEnvironment " sv ;
}
2023-12-16 17:49:34 +03:30
ByteString CreateVariable : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
auto mode_string = m_mode = = EnvironmentMode : : Lexical ? " Lexical " : " Variable " ;
2024-05-06 10:12:02 +02:00
return ByteString : : formatted ( " CreateVariable env:{} immutable:{} global:{} {} " , mode_string , m_is_immutable , m_is_global , executable . identifier_table - > get ( m_identifier ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-05-05 22:06:55 +02:00
ByteString CreateRestParams : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
return ByteString : : formatted ( " CreateRestParams {}, rest_index:{} " , format_operand ( " dst " sv , m_dst , executable ) , m_rest_index ) ;
}
2024-05-21 09:32:51 +01:00
ByteString CreateArguments : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2024-05-05 22:06:55 +02:00
{
2024-05-21 09:32:51 +01:00
StringBuilder builder ;
builder . appendff ( " CreateArguments " ) ;
if ( m_dst . has_value ( ) )
builder . appendff ( " {} " , format_operand ( " dst " sv , * m_dst , executable ) ) ;
builder . appendff ( " {} immutable:{} " , m_kind = = Kind : : Mapped ? " mapped " sv : " unmapped " sv , m_is_immutable ) ;
return builder . to_byte_string ( ) ;
2024-05-05 22:06:55 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString EnterObjectEnvironment : : to_byte_string_impl ( Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " EnterObjectEnvironment {} " ,
format_operand ( " object " sv , m_object , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-05-14 11:30:30 +02:00
ByteString InitializeLexicalBinding : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-05-14 11:30:30 +02:00
return ByteString : : formatted ( " InitializeLexicalBinding {}, {} " ,
2024-02-04 08:00:54 +01:00
executable . identifier_table - > get ( m_identifier ) ,
2024-05-14 11:30:30 +02:00
format_operand ( " src " sv , src ( ) , executable ) ) ;
}
ByteString InitializeVariableBinding : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
return ByteString : : formatted ( " InitializeVariableBinding {}, {} " ,
executable . identifier_table - > get ( m_identifier ) ,
format_operand ( " src " sv , src ( ) , executable ) ) ;
}
ByteString SetLexicalBinding : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
return ByteString : : formatted ( " SetLexicalBinding {}, {} " ,
executable . identifier_table - > get ( m_identifier ) ,
format_operand ( " src " sv , src ( ) , executable ) ) ;
}
ByteString SetVariableBinding : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
return ByteString : : formatted ( " SetVariableBinding {}, {} " ,
executable . identifier_table - > get ( m_identifier ) ,
format_operand ( " src " sv , src ( ) , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2025-10-10 11:16:02 +02:00
static StringView property_kind_to_string ( PutKind kind )
2023-09-27 10:10:00 +02:00
{
switch ( kind ) {
2025-10-10 11:16:02 +02:00
case PutKind : : Getter :
2023-09-27 10:10:00 +02:00
return " getter " sv ;
2025-10-10 11:16:02 +02:00
case PutKind : : Setter :
2023-09-27 10:10:00 +02:00
return " setter " sv ;
2025-10-10 11:16:02 +02:00
case PutKind : : Normal :
2023-09-27 10:10:00 +02:00
return " key-value " sv ;
2025-10-10 11:16:02 +02:00
case PutKind : : Own :
2023-09-27 10:10:00 +02:00
return " direct-key-value " sv ;
2025-10-10 11:16:02 +02:00
case PutKind : : Prototype :
2023-09-27 10:10:00 +02:00
return " proto-setter " sv ;
}
VERIFY_NOT_REACHED ( ) ;
}
2024-11-01 22:00:32 +01:00
ByteString PutBySpread : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
return ByteString : : formatted ( " PutBySpread {}, {} " ,
format_operand ( " base " sv , m_base , executable ) ,
format_operand ( " src " sv , m_src , executable ) ) ;
}
2023-12-16 17:49:34 +03:30
ByteString PutById : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
auto kind = property_kind_to_string ( m_kind ) ;
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " PutById {}, {}, {}, kind:{} " ,
format_operand ( " base " sv , m_base , executable ) ,
executable . identifier_table - > get ( m_property ) ,
format_operand ( " src " sv , m_src , executable ) ,
kind ) ;
2023-09-27 10:10:00 +02:00
}
2025-09-13 16:20:41 +02:00
ByteString PutByNumericId : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
auto kind = property_kind_to_string ( m_kind ) ;
return ByteString : : formatted ( " PutByNumericId {}, {}, {}, kind:{} " ,
format_operand ( " base " sv , m_base , executable ) ,
m_property_index ,
format_operand ( " src " sv , m_src , executable ) ,
kind ) ;
}
2023-12-16 17:49:34 +03:30
ByteString PutByIdWithThis : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
auto kind = property_kind_to_string ( m_kind ) ;
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " PutByIdWithThis {}, {}, {}, {}, kind:{} " ,
format_operand ( " base " sv , m_base , executable ) ,
executable . identifier_table - > get ( m_property ) ,
format_operand ( " src " sv , m_src , executable ) ,
format_operand ( " this " sv , m_this_value , executable ) ,
kind ) ;
2023-09-27 10:10:00 +02:00
}
2023-12-16 17:49:34 +03:30
ByteString PutPrivateById : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
auto kind = property_kind_to_string ( m_kind ) ;
2024-02-04 08:00:54 +01:00
return ByteString : : formatted (
" PutPrivateById {}, {}, {}, kind:{} " ,
format_operand ( " base " sv , m_base , executable ) ,
executable . identifier_table - > get ( m_property ) ,
format_operand ( " src " sv , m_src , executable ) ,
kind ) ;
2023-09-27 10:10:00 +02:00
}
2023-12-16 17:49:34 +03:30
ByteString GetById : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " GetById {}, {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " base " sv , m_base , executable ) ,
executable . identifier_table - > get ( m_property ) ) ;
2023-09-27 10:10:00 +02:00
}
2023-12-16 17:49:34 +03:30
ByteString GetByIdWithThis : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " GetByIdWithThis {}, {}, {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " base " sv , m_base , executable ) ,
executable . identifier_table - > get ( m_property ) ,
format_operand ( " this " sv , m_this_value , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-05-20 11:53:28 +02:00
ByteString GetLength : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
return ByteString : : formatted ( " GetLength {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " base " sv , m_base , executable ) ) ;
}
ByteString GetLengthWithThis : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
return ByteString : : formatted ( " GetLengthWithThis {}, {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " base " sv , m_base , executable ) ,
format_operand ( " this " sv , m_this_value , executable ) ) ;
}
2023-12-16 17:49:34 +03:30
ByteString GetPrivateById : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " GetPrivateById {}, {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " base " sv , m_base , executable ) ,
executable . identifier_table - > get ( m_property ) ) ;
2023-09-27 10:10:00 +02:00
}
2023-12-16 17:49:34 +03:30
ByteString HasPrivateId : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " HasPrivateId {}, {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " base " sv , m_base , executable ) ,
executable . identifier_table - > get ( m_property ) ) ;
2023-09-27 10:10:00 +02:00
}
2023-12-16 17:49:34 +03:30
ByteString DeleteById : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " DeleteById {}, {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " base " sv , m_base , executable ) ,
executable . identifier_table - > get ( m_property ) ) ;
2023-09-27 10:10:00 +02:00
}
2023-12-16 17:49:34 +03:30
ByteString DeleteByIdWithThis : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " DeleteByIdWithThis {}, {}, {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " base " sv , m_base , executable ) ,
executable . identifier_table - > get ( m_property ) ,
format_operand ( " this " sv , m_this_value , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2023-12-16 17:49:34 +03:30
ByteString Jump : : to_byte_string_impl ( Bytecode : : Executable const & ) const
2023-09-27 10:10:00 +02:00
{
2024-05-05 11:52:14 +02:00
return ByteString : : formatted ( " Jump {} " , m_target ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString JumpIf : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " JumpIf {}, \033 [32mtrue \033 [0m:{} \033 [32mfalse \033 [0m:{} " ,
format_operand ( " condition " sv , m_condition , executable ) ,
2024-05-05 11:52:14 +02:00
m_true_target ,
m_false_target ) ;
2023-09-27 10:10:00 +02:00
}
2024-05-06 10:15:17 +02:00
ByteString JumpTrue : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
return ByteString : : formatted ( " JumpTrue {}, {} " ,
format_operand ( " condition " sv , m_condition , executable ) ,
m_target ) ;
}
ByteString JumpFalse : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
return ByteString : : formatted ( " JumpFalse {}, {} " ,
format_operand ( " condition " sv , m_condition , executable ) ,
m_target ) ;
}
2024-02-04 08:00:54 +01:00
ByteString JumpNullish : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " JumpNullish {}, null:{} nonnull:{} " ,
format_operand ( " condition " sv , m_condition , executable ) ,
2024-05-05 11:52:14 +02:00
m_true_target ,
m_false_target ) ;
2023-09-27 10:10:00 +02:00
}
2024-05-13 09:23:53 +02:00
# define HANDLE_COMPARISON_OP(op_TitleCase, op_snake_case, numeric_operator) \
2024-05-09 15:13:31 +02:00
ByteString Jump # # op_TitleCase : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const \
{ \
return ByteString : : formatted ( " Jump " # op_TitleCase " {}, {}, true:{}, false:{} " , \
format_operand ( " lhs " sv , m_lhs , executable ) , \
format_operand ( " rhs " sv , m_rhs , executable ) , \
m_true_target , \
m_false_target ) ; \
}
JS_ENUMERATE_COMPARISON_OPS ( HANDLE_COMPARISON_OP )
2024-02-04 08:00:54 +01:00
ByteString JumpUndefined : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " JumpUndefined {}, undefined:{} defined:{} " ,
format_operand ( " condition " sv , m_condition , executable ) ,
2024-05-05 11:52:14 +02:00
m_true_target ,
m_false_target ) ;
2023-09-27 10:10:00 +02:00
}
2023-12-16 17:49:34 +03:30
ByteString Call : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-10-31 22:47:30 +01:00
StringBuilder builder ;
2025-04-08 16:54:29 +02:00
builder . appendff ( " Call {}, {}, {}, " ,
2024-10-31 22:47:30 +01:00
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " callee " sv , m_callee , executable ) ,
format_operand ( " this " sv , m_this_value , executable ) ) ;
builder . append ( format_operand_list ( " args " sv , { m_arguments , m_argument_count } , executable ) ) ;
2024-02-20 15:59:45 +01:00
2024-10-31 22:47:30 +01:00
if ( m_expression_string . has_value ( ) ) {
builder . appendff ( " , `{}` " , executable . get_string ( m_expression_string . value ( ) ) ) ;
}
return builder . to_byte_string ( ) ;
}
ByteString CallConstruct : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
2024-02-20 15:59:45 +01:00
StringBuilder builder ;
2025-04-08 16:54:29 +02:00
builder . appendff ( " CallConstruct {}, {}, " ,
2024-10-31 22:47:30 +01:00
format_operand ( " dst " sv , m_dst , executable ) ,
2025-04-06 22:50:55 +02:00
format_operand ( " callee " sv , m_callee , executable ) ) ;
2024-10-31 22:47:30 +01:00
builder . append ( format_operand_list ( " args " sv , { m_arguments , m_argument_count } , executable ) ) ;
if ( m_expression_string . has_value ( ) ) {
builder . appendff ( " , `{}` " , executable . get_string ( m_expression_string . value ( ) ) ) ;
}
return builder . to_byte_string ( ) ;
}
ByteString CallDirectEval : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
StringBuilder builder ;
2025-04-08 16:54:29 +02:00
builder . appendff ( " CallDirectEval {}, {}, {}, " ,
2024-02-20 15:59:45 +01:00
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " callee " sv , m_callee , executable ) ,
format_operand ( " this " sv , m_this_value , executable ) ) ;
builder . append ( format_operand_list ( " args " sv , { m_arguments , m_argument_count } , executable ) ) ;
2024-10-31 22:47:30 +01:00
if ( m_expression_string . has_value ( ) ) {
builder . appendff ( " , `{}` " , executable . get_string ( m_expression_string . value ( ) ) ) ;
2024-02-04 08:00:54 +01:00
}
2024-02-20 15:59:45 +01:00
2024-10-31 22:47:30 +01:00
return builder . to_byte_string ( ) ;
}
ByteString CallBuiltin : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
StringBuilder builder ;
2025-04-08 16:54:29 +02:00
builder . appendff ( " CallBuiltin {}, {}, {}, " ,
2024-10-31 22:47:30 +01:00
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " callee " sv , m_callee , executable ) ,
format_operand ( " this " sv , m_this_value , executable ) ) ;
builder . append ( format_operand_list ( " args " sv , { m_arguments , m_argument_count } , executable ) ) ;
builder . appendff ( " , (builtin:{}) " , m_builtin ) ;
2024-02-04 08:00:54 +01:00
if ( m_expression_string . has_value ( ) ) {
2024-02-20 15:59:45 +01:00
builder . appendff ( " , `{}` " , executable . get_string ( m_expression_string . value ( ) ) ) ;
2024-02-04 08:00:54 +01:00
}
2024-02-20 15:59:45 +01:00
return builder . to_byte_string ( ) ;
2023-09-27 10:10:00 +02:00
}
2023-12-16 17:49:34 +03:30
ByteString CallWithArgumentArray : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
StringBuilder builder ;
2025-08-30 11:00:54 +02:00
builder . appendff ( " CallWithArgumentArray {}, {}, {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " callee " sv , m_callee , executable ) ,
format_operand ( " this " sv , m_this_value , executable ) ,
format_operand ( " arguments " sv , m_arguments , executable ) ) ;
if ( m_expression_string . has_value ( ) )
builder . appendff ( " ({}) " , executable . get_string ( m_expression_string . value ( ) ) ) ;
return builder . to_byte_string ( ) ;
}
ByteString CallDirectEvalWithArgumentArray : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
StringBuilder builder ;
builder . appendff ( " CallDirectEvalWithArgumentArray {}, {}, {}, {} " ,
2024-02-04 08:00:54 +01:00
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " callee " sv , m_callee , executable ) ,
format_operand ( " this " sv , m_this_value , executable ) ,
format_operand ( " arguments " sv , m_arguments , executable ) ) ;
2023-09-27 10:10:00 +02:00
if ( m_expression_string . has_value ( ) )
2024-02-04 08:00:54 +01:00
builder . appendff ( " ({}) " , executable . get_string ( m_expression_string . value ( ) ) ) ;
return builder . to_byte_string ( ) ;
2023-09-27 10:10:00 +02:00
}
2025-08-30 11:00:54 +02:00
ByteString CallConstructWithArgumentArray : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
StringBuilder builder ;
builder . appendff ( " CallConstructWithArgumentArray {}, {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " callee " sv , m_callee , executable ) ,
format_operand ( " arguments " sv , m_arguments , executable ) ) ;
if ( m_expression_string . has_value ( ) )
builder . appendff ( " ({}) " , executable . get_string ( m_expression_string . value ( ) ) ) ;
return builder . to_byte_string ( ) ;
}
2024-02-04 08:00:54 +01:00
ByteString SuperCallWithArgumentArray : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " SuperCallWithArgumentArray {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " arguments " sv , m_arguments , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString NewFunction : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
StringBuilder builder ;
2024-02-04 08:00:54 +01:00
builder . appendff ( " NewFunction {} " ,
format_operand ( " dst " sv , m_dst , executable ) ) ;
2023-09-27 10:10:00 +02:00
if ( m_function_node . has_name ( ) )
2025-04-08 16:54:29 +02:00
builder . appendff ( " name:{} " , m_function_node . name ( ) ) ;
2023-09-27 10:10:00 +02:00
if ( m_lhs_name . has_value ( ) )
2025-04-08 16:54:29 +02:00
builder . appendff ( " lhs_name:{} " , executable . get_identifier ( m_lhs_name . value ( ) ) ) ;
2023-09-27 10:10:00 +02:00
if ( m_home_object . has_value ( ) )
2025-04-08 16:54:29 +02:00
builder . appendff ( " , {} " , format_operand ( " home_object " sv , m_home_object . value ( ) , executable ) ) ;
2023-12-16 17:49:34 +03:30
return builder . to_byte_string ( ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString NewClass : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
StringBuilder builder ;
auto name = m_class_expression . name ( ) ;
2024-02-04 08:00:54 +01:00
builder . appendff ( " NewClass {} " ,
format_operand ( " dst " sv , m_dst , executable ) ) ;
if ( m_super_class . has_value ( ) )
builder . appendff ( " , {} " , format_operand ( " super_class " sv , * m_super_class , executable ) ) ;
if ( ! name . is_empty ( ) )
builder . appendff ( " , {} " , name ) ;
2023-09-27 10:10:00 +02:00
if ( m_lhs_name . has_value ( ) )
2025-04-08 16:54:29 +02:00
builder . appendff ( " , lhs_name:{} " , executable . get_identifier ( m_lhs_name . value ( ) ) ) ;
2023-12-16 17:49:34 +03:30
return builder . to_byte_string ( ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString Return : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2025-05-16 12:28:51 +12:00
return ByteString : : formatted ( " Return {} " , format_operand ( " value " sv , m_value , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString Increment : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " Increment {} " , format_operand ( " dst " sv , m_dst , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-20 11:45:01 +01:00
ByteString PostfixIncrement : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
return ByteString : : formatted ( " PostfixIncrement {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
2024-03-03 09:04:30 +01:00
format_operand ( " src " sv , m_src , executable ) ) ;
2024-02-20 11:45:01 +01:00
}
ByteString Decrement : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
return ByteString : : formatted ( " Decrement {} " , format_operand ( " dst " sv , m_dst , executable ) ) ;
}
ByteString PostfixDecrement : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-20 11:45:01 +01:00
return ByteString : : formatted ( " PostfixDecrement {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
2024-03-03 09:04:30 +01:00
format_operand ( " src " sv , m_src , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString Throw : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " Throw {} " ,
format_operand ( " src " sv , m_src , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString ThrowIfNotObject : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " ThrowIfNotObject {} " ,
format_operand ( " src " sv , m_src , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString ThrowIfNullish : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " ThrowIfNullish {} " ,
format_operand ( " src " sv , m_src , executable ) ) ;
}
ByteString ThrowIfTDZ : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
return ByteString : : formatted ( " ThrowIfTDZ {} " ,
format_operand ( " src " sv , m_src , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2023-12-16 17:49:34 +03:30
ByteString EnterUnwindContext : : to_byte_string_impl ( Bytecode : : Executable const & ) const
2023-09-27 10:10:00 +02:00
{
2023-12-16 17:49:34 +03:30
return ByteString : : formatted ( " EnterUnwindContext entry:{} " , m_entry_point ) ;
2023-09-27 10:10:00 +02:00
}
2023-12-16 17:49:34 +03:30
ByteString ScheduleJump : : to_byte_string_impl ( Bytecode : : Executable const & ) const
2023-09-27 10:10:00 +02:00
{
2023-12-16 17:49:34 +03:30
return ByteString : : formatted ( " ScheduleJump {} " , m_target ) ;
2023-09-27 10:10:00 +02:00
}
2023-12-16 17:49:34 +03:30
ByteString LeaveLexicalEnvironment : : to_byte_string_impl ( Bytecode : : Executable const & ) const
2023-09-27 10:10:00 +02:00
{
return " LeaveLexicalEnvironment " sv ;
}
2024-05-11 22:54:41 +00:00
ByteString LeavePrivateEnvironment : : to_byte_string_impl ( Bytecode : : Executable const & ) const
{
return " LeavePrivateEnvironment " sv ;
}
2023-12-16 17:49:34 +03:30
ByteString LeaveUnwindContext : : to_byte_string_impl ( Bytecode : : Executable const & ) const
2023-09-27 10:10:00 +02:00
{
return " LeaveUnwindContext " ;
}
2023-12-16 17:49:34 +03:30
ByteString ContinuePendingUnwind : : to_byte_string_impl ( Bytecode : : Executable const & ) const
2023-09-27 10:10:00 +02:00
{
2023-12-16 17:49:34 +03:30
return ByteString : : formatted ( " ContinuePendingUnwind resume:{} " , m_resume_target ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString Yield : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
if ( m_continuation_label . has_value ( ) ) {
2024-05-06 06:44:08 +02:00
return ByteString : : formatted ( " Yield continuation:{}, {} " ,
m_continuation_label . value ( ) ,
2024-02-04 08:00:54 +01:00
format_operand ( " value " sv , m_value , executable ) ) ;
}
return ByteString : : formatted ( " Yield return {} " ,
format_operand ( " value " sv , m_value , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-05-18 17:25:43 +02:00
ByteString PrepareYield : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
return ByteString : : formatted ( " PrepareYield {}, {} " ,
format_operand ( " dst " sv , m_dest , executable ) ,
format_operand ( " value " sv , m_value , executable ) ) ;
}
2024-02-04 08:00:54 +01:00
ByteString Await : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-05-06 06:44:08 +02:00
return ByteString : : formatted ( " Await {}, continuation:{} " ,
2024-02-04 08:00:54 +01:00
format_operand ( " argument " sv , m_argument , executable ) ,
2024-05-06 06:44:08 +02:00
m_continuation_label ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString GetByValue : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " GetByValue {}, {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " base " sv , m_base , executable ) ,
format_operand ( " property " sv , m_property , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString GetByValueWithThis : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " GetByValueWithThis {}, {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " base " sv , m_base , executable ) ,
format_operand ( " property " sv , m_property , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString PutByValue : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
auto kind = property_kind_to_string ( m_kind ) ;
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " PutByValue {}, {}, {}, kind:{} " ,
format_operand ( " base " sv , m_base , executable ) ,
format_operand ( " property " sv , m_property , executable ) ,
format_operand ( " src " sv , m_src , executable ) ,
kind ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString PutByValueWithThis : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
auto kind = property_kind_to_string ( m_kind ) ;
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " PutByValueWithThis {}, {}, {}, {}, kind:{} " ,
format_operand ( " base " sv , m_base , executable ) ,
format_operand ( " property " sv , m_property , executable ) ,
format_operand ( " src " sv , m_src , executable ) ,
format_operand ( " this " sv , m_this_value , executable ) ,
kind ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString DeleteByValue : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " DeleteByValue {}, {}, {} " ,
format_operand ( " dst " sv , dst ( ) , executable ) ,
format_operand ( " base " sv , m_base , executable ) ,
format_operand ( " property " sv , m_property , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString DeleteByValueWithThis : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " DeleteByValueWithThis {}, {}, {}, {} " ,
format_operand ( " dst " sv , dst ( ) , executable ) ,
format_operand ( " base " sv , m_base , executable ) ,
format_operand ( " property " sv , m_property , executable ) ,
format_operand ( " this " sv , m_this_value , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString GetIterator : : to_byte_string_impl ( Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
auto hint = m_hint = = IteratorHint : : Sync ? " sync " : " async " ;
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " GetIterator {}, {}, hint:{} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " iterable " sv , m_iterable , executable ) ,
hint ) ;
2023-09-27 10:10:00 +02:00
}
2023-12-16 17:49:34 +03:30
ByteString GetMethod : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " GetMethod {}, {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " object " sv , m_object , executable ) ,
executable . identifier_table - > get ( m_property ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString GetObjectPropertyIterator : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " GetObjectPropertyIterator {}, {} " ,
format_operand ( " dst " sv , dst ( ) , executable ) ,
format_operand ( " object " sv , object ( ) , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString IteratorClose : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
if ( ! m_completion_value . has_value ( ) )
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " IteratorClose {}, completion_type={} completion_value=<empty> " ,
format_operand ( " iterator_record " sv , m_iterator_record , executable ) ,
to_underlying ( m_completion_type ) ) ;
2023-09-27 10:10:00 +02:00
auto completion_value_string = m_completion_value - > to_string_without_side_effects ( ) ;
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " IteratorClose {}, completion_type={} completion_value={} " ,
format_operand ( " iterator_record " sv , m_iterator_record , executable ) ,
to_underlying ( m_completion_type ) , completion_value_string ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString AsyncIteratorClose : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
if ( ! m_completion_value . has_value ( ) ) {
return ByteString : : formatted ( " AsyncIteratorClose {}, completion_type:{} completion_value:<empty> " ,
format_operand ( " iterator_record " sv , m_iterator_record , executable ) ,
to_underlying ( m_completion_type ) ) ;
}
2023-09-27 10:10:00 +02:00
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " AsyncIteratorClose {}, completion_type:{}, completion_value:{} " ,
format_operand ( " iterator_record " sv , m_iterator_record , executable ) ,
to_underlying ( m_completion_type ) , m_completion_value ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString IteratorNext : : to_byte_string_impl ( Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " IteratorNext {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " iterator_record " sv , m_iterator_record , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2025-05-01 16:05:24 +03:00
ByteString IteratorNextUnpack : : to_byte_string_impl ( Executable const & executable ) const
2025-04-30 16:31:26 +03:00
{
2025-05-01 16:05:24 +03:00
return ByteString : : formatted ( " IteratorNextUnpack {}, {}, {} " ,
2025-04-30 16:31:26 +03:00
format_operand ( " dst_value " sv , m_dst_value , executable ) ,
format_operand ( " dst_done " sv , m_dst_done , executable ) ,
format_operand ( " iterator_record " sv , m_iterator_record , executable ) ) ;
}
2024-05-31 20:41:29 +02:00
ByteString ResolveThisBinding : : to_byte_string_impl ( Bytecode : : Executable const & ) const
2023-09-27 10:10:00 +02:00
{
2024-05-31 20:41:29 +02:00
return " ResolveThisBinding " sv ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString ResolveSuperBase : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " ResolveSuperBase {} " ,
format_operand ( " dst " sv , m_dst , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString GetNewTarget : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " GetNewTarget {} " , format_operand ( " dst " sv , m_dst , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString GetImportMeta : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " GetImportMeta {} " , format_operand ( " dst " sv , m_dst , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-06-14 09:37:26 +02:00
ByteString TypeofBinding : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-06-14 09:37:26 +02:00
return ByteString : : formatted ( " TypeofBinding {}, {} " ,
2024-02-04 08:00:54 +01:00
format_operand ( " dst " sv , m_dst , executable ) ,
executable . identifier_table - > get ( m_identifier ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString BlockDeclarationInstantiation : : to_byte_string_impl ( Bytecode : : Executable const & ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return " BlockDeclarationInstantiation " sv ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString ImportCall : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " ImportCall {}, {}, {} " ,
format_operand ( " dst " sv , m_dst , executable ) ,
format_operand ( " specifier " sv , m_specifier , executable ) ,
format_operand ( " options " sv , m_options , executable ) ) ;
}
ByteString Catch : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
return ByteString : : formatted ( " Catch {} " ,
format_operand ( " dst " sv , m_dst , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-04-11 11:58:18 +02:00
ByteString LeaveFinally : : to_byte_string_impl ( Bytecode : : Executable const & ) const
{
return ByteString : : formatted ( " LeaveFinally " ) ;
}
2024-04-11 11:07:35 +02:00
ByteString RestoreScheduledJump : : to_byte_string_impl ( Bytecode : : Executable const & ) const
{
return ByteString : : formatted ( " RestoreScheduledJump " ) ;
}
2024-02-04 08:00:54 +01:00
ByteString GetObjectFromIteratorRecord : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-09-27 10:10:00 +02:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " GetObjectFromIteratorRecord {}, {} " ,
format_operand ( " object " sv , m_object , executable ) ,
format_operand ( " iterator_record " sv , m_iterator_record , executable ) ) ;
2023-09-27 10:10:00 +02:00
}
2024-02-04 08:00:54 +01:00
ByteString GetNextMethodFromIteratorRecord : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-11-11 23:19:46 +01:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " GetNextMethodFromIteratorRecord {}, {} " ,
format_operand ( " next_method " sv , m_next_method , executable ) ,
format_operand ( " iterator_record " sv , m_iterator_record , executable ) ) ;
2023-11-11 23:19:46 +01:00
}
2024-02-04 08:00:54 +01:00
ByteString End : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-12-07 10:44:41 +01:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " End {} " , format_operand ( " value " sv , m_value , executable ) ) ;
2023-12-07 10:44:41 +01:00
}
2024-02-04 08:00:54 +01:00
ByteString Dump : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
2023-12-07 10:44:41 +01:00
{
2024-02-04 08:00:54 +01:00
return ByteString : : formatted ( " Dump '{}', {} " , m_text ,
format_operand ( " value " sv , m_value , executable ) ) ;
2023-12-07 10:44:41 +01:00
}
2025-04-29 16:08:42 +02:00
void GetCompletionFields : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2025-03-31 09:32:39 +01:00
{
2025-04-29 16:08:42 +02:00
auto const & completion_cell = static_cast < CompletionCell const & > ( interpreter . get ( m_completion ) . as_cell ( ) ) ;
interpreter . set ( m_value_dst , completion_cell . completion ( ) . value ( ) ) ;
interpreter . set ( m_type_dst , Value ( to_underlying ( completion_cell . completion ( ) . type ( ) ) ) ) ;
2025-03-31 09:32:39 +01:00
}
ByteString GetCompletionFields : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
return ByteString : : formatted ( " GetCompletionFields {}, {}, {} " ,
format_operand ( " value_dst " sv , m_value_dst , executable ) ,
format_operand ( " type_dst " sv , m_type_dst , executable ) ,
format_operand ( " completion " sv , m_completion , executable ) ) ;
}
2025-04-29 16:08:42 +02:00
void SetCompletionType : : execute_impl ( Bytecode : : Interpreter & interpreter ) const
2025-03-31 09:32:39 +01:00
{
2025-04-29 16:08:42 +02:00
auto & completion_cell = static_cast < CompletionCell & > ( interpreter . get ( m_completion ) . as_cell ( ) ) ;
2025-03-31 09:32:39 +01:00
auto completion = completion_cell . completion ( ) ;
2025-04-05 14:06:33 +02:00
completion_cell . set_completion ( Completion { m_type , completion . value ( ) } ) ;
2025-03-31 09:32:39 +01:00
}
ByteString SetCompletionType : : to_byte_string_impl ( Bytecode : : Executable const & executable ) const
{
return ByteString : : formatted ( " SetCompletionType {}, type={} " ,
format_operand ( " completion " sv , m_completion , executable ) ,
to_underlying ( m_type ) ) ;
}
2023-09-27 10:10:00 +02:00
}