2026-06-14 11:07:00 -03:00
/*
* Copyright ( c ) 2026 , the Ladybird developers .
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <AK/ScopeGuard.h>
2026-06-14 11:07:07 -03:00
# include <AK/Types.h>
2026-06-14 11:07:00 -03:00
# include <LibJS/Bytecode/Builtins.h>
# include <LibJS/Bytecode/Instruction.h>
# include <LibJS/Bytecode/Op.h>
# include <LibJS/Bytecode/PropertyAccess.h>
# include <LibJS/Bytecode/PropertyNameIterator.h>
# include <LibJS/Runtime/AbstractOperations.h>
# include <LibJS/Runtime/Array.h>
# include <LibJS/Runtime/AsyncFromSyncIteratorPrototype.h>
# include <LibJS/Runtime/AsyncGenerator.h>
# include <LibJS/Runtime/ClassConstruction.h>
# include <LibJS/Runtime/DeclarativeEnvironment.h>
# include <LibJS/Runtime/ECMAScriptFunctionObject.h>
# include <LibJS/Runtime/Error.h>
# include <LibJS/Runtime/FunctionEnvironment.h>
# include <LibJS/Runtime/GeneratorObject.h>
# include <LibJS/Runtime/GlobalEnvironment.h>
# include <LibJS/Runtime/Iterator.h>
# include <LibJS/Runtime/MathObject.h>
# include <LibJS/Runtime/ModuleEnvironment.h>
# include <LibJS/Runtime/Object.h>
# include <LibJS/Runtime/ObjectEnvironment.h>
# include <LibJS/Runtime/PrimitiveString.h>
# include <LibJS/Runtime/PrivateEnvironment.h>
# include <LibJS/Runtime/Reference.h>
# include <LibJS/Runtime/RegExpObject.h>
# include <LibJS/Runtime/StringConstructor.h>
# include <LibJS/Runtime/TypedArray.h>
# include <LibJS/Runtime/VM.h>
# include <LibJS/Runtime/Value.h>
# include <LibJS/Runtime/ValueInlines.h>
# include <math.h>
// ===== Slow path functions callable from assembly =====
// All slow path functions follow the same convention:
2026-06-14 09:30:56 -03:00
// i64 func(VM* vm, u32 pc, Op::Foo const* instruction)
2026-06-14 11:07:00 -03:00
// Returns >= 0: new program counter to dispatch to
// Returns < 0: should exit the asm interpreter
using namespace JS ;
using namespace JS : : Bytecode ;
static i64 handle_asm_exception ( VM & vm , u32 pc , Value exception )
{
auto response = vm . handle_exception ( pc , exception ) ;
if ( response = = VM : : HandleExceptionResponse : : ExitFromExecutable )
return - 1 ;
// ContinueInThisExecutable: new pc is in the execution context
return static_cast < i64 > ( vm . running_execution_context ( ) . program_counter ) ;
}
# define ASM_TRY(vm, pc, expression) \
( { \
auto & asm_try_vm = ( vm ) ; \
auto asm_try_pc = ( pc ) ; \
auto & & asm_try_result = ( expression ) ; \
if ( asm_try_result . is_error ( ) ) [[unlikely]] \
return handle_asm_exception ( asm_try_vm , asm_try_pc , asm_try_result . release_error ( ) . value ( ) ) ; \
asm_try_result . release_value ( ) ; \
} )
2026-06-14 09:30:56 -03:00
template < typename Op >
static i64 advance_or_continue ( u32 pc , i64 next_pc )
{
if ( next_pc ! = static_cast < i64 > ( pc ) )
return next_pc ;
return static_cast < i64 > ( pc + sizeof ( Op ) ) ;
}
2026-06-14 11:07:00 -03:00
template < typename EnvironmentPointer >
static EnvironmentPointer asm_get_cacheable_environment ( EnvironmentPointer environment , EnvironmentCoordinate const & cache )
{
VERIFY ( cache . is_valid ( ) ) ;
for ( size_t i = 0 ; i < cache . hops ; + + i ) {
if ( ! environment - > is_declarative_environment ( ) | | environment - > is_permanently_screwed_by_eval ( ) ) [[unlikely]]
return nullptr ;
environment = environment - > outer_environment ( ) ;
if ( ! environment ) [[unlikely]]
return nullptr ;
}
if ( environment - > is_declarative_environment ( ) & & ! environment - > is_permanently_screwed_by_eval ( ) ) [[likely]]
return environment ;
return nullptr ;
}
template < typename EnvironmentPointer >
static EnvironmentPointer asm_get_cached_environment ( EnvironmentPointer environment , EnvironmentCoordinate & cache )
{
if ( ! cache . is_valid ( ) ) [[unlikely]]
return nullptr ;
if ( auto * cached_environment = asm_get_cacheable_environment ( environment , cache ) ) [[likely]]
return cached_environment ;
cache = { } ;
return nullptr ;
}
template < typename EnvironmentPointer >
static void asm_update_environment_coordinate_cache ( EnvironmentPointer environment , Reference const & reference , EnvironmentCoordinate & cache )
{
if ( ! reference . environment_coordinate ( ) . has_value ( ) )
return ;
auto candidate = reference . environment_coordinate ( ) . value ( ) ;
if ( asm_get_cacheable_environment ( environment , candidate ) )
cache = candidate ;
}
enum class AsmBindingIsKnownToBeInitialized {
No ,
Yes ,
} ;
template < AsmBindingIsKnownToBeInitialized binding_is_known_to_be_initialized >
static i64 asm_get_binding ( VM & vm , u32 pc , Operand dst , EnvironmentCoordinate const & cache )
{
VERIFY ( cache . is_valid ( ) ) ;
auto const * environment = vm . running_execution_context ( ) . lexical_environment . ptr ( ) ;
for ( size_t i = 0 ; i < cache . hops ; + + i )
environment = environment - > outer_environment ( ) ;
Value value ;
if constexpr ( binding_is_known_to_be_initialized = = AsmBindingIsKnownToBeInitialized : : No ) {
value = ASM_TRY ( vm , pc , 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 ) ;
}
vm . set ( dst , value ) ;
return static_cast < i64 > ( pc ) ;
}
template < AsmBindingIsKnownToBeInitialized binding_is_known_to_be_initialized >
static i64 asm_dynamic_get_binding ( VM & vm , u32 pc , Operand dst , IdentifierTableIndex identifier_index , Strict strict , EnvironmentCoordinate & cache )
{
auto const * current_environment = vm . running_execution_context ( ) . lexical_environment . ptr ( ) ;
if ( auto const * cached_environment = asm_get_cached_environment ( current_environment , cache ) ) [[likely]] {
Value value ;
if constexpr ( binding_is_known_to_be_initialized = = AsmBindingIsKnownToBeInitialized : : No ) {
value = ASM_TRY ( vm , pc , static_cast < DeclarativeEnvironment const & > ( * cached_environment ) . get_binding_value_direct ( vm , cache . index ) ) ;
} else {
value = static_cast < DeclarativeEnvironment const & > ( * cached_environment ) . get_initialized_binding_value_direct ( cache . index ) ;
}
vm . set ( dst , value ) ;
return static_cast < i64 > ( pc ) ;
}
auto & executable = vm . current_executable ( ) ;
auto reference = ASM_TRY ( vm , pc , vm . resolve_binding ( executable . get_identifier ( identifier_index ) , strict ) ) ;
asm_update_environment_coordinate_cache ( current_environment , reference , cache ) ;
vm . set ( dst , ASM_TRY ( vm , pc , reference . get_value ( vm ) ) ) ;
return static_cast < i64 > ( pc ) ;
}
static i64 asm_dynamic_get_callee_and_this_from_environment ( VM & vm , u32 pc , Operand callee_dst , Operand this_value_dst , IdentifierTableIndex identifier_index , Strict strict , EnvironmentCoordinate & cache )
{
auto const * current_environment = vm . running_execution_context ( ) . lexical_environment . ptr ( ) ;
if ( auto const * cached_environment = asm_get_cached_environment ( current_environment , cache ) ) [[likely]] {
auto callee = ASM_TRY ( vm , pc , static_cast < DeclarativeEnvironment const & > ( * cached_environment ) . get_binding_value_direct ( vm , cache . index ) ) ;
vm . set ( callee_dst , callee ) ;
vm . set ( this_value_dst , js_undefined ( ) ) ;
return static_cast < i64 > ( pc ) ;
}
auto reference = ASM_TRY ( vm , pc , vm . resolve_binding ( vm . get_identifier ( identifier_index ) , strict ) ) ;
asm_update_environment_coordinate_cache ( current_environment , reference , cache ) ;
auto callee = ASM_TRY ( vm , pc , reference . get_value ( vm ) ) ;
Value this_value ;
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 ( ) ) [[unlikely]]
this_value = base_object ;
}
}
vm . set ( callee_dst , callee ) ;
vm . set ( this_value_dst , this_value ) ;
return static_cast < i64 > ( pc ) ;
}
template < Op : : EnvironmentMode environment_mode , Op : : BindingInitializationMode initialization_mode >
static i64 asm_initialize_or_set_binding ( VM & vm , u32 pc , Strict strict , Value value , EnvironmentCoordinate const & cache )
{
VERIFY ( cache . is_valid ( ) ) ;
auto * environment = environment_mode = = Op : : EnvironmentMode : : Lexical
? vm . running_execution_context ( ) . lexical_environment . ptr ( )
: vm . running_execution_context ( ) . variable_environment . ptr ( ) ;
for ( size_t i = 0 ; i < cache . hops ; + + i )
environment = environment - > outer_environment ( ) ;
if constexpr ( initialization_mode = = Op : : BindingInitializationMode : : Initialize ) {
ASM_TRY ( vm , pc , static_cast < DeclarativeEnvironment & > ( * environment ) . initialize_binding_direct ( vm , cache . index , value , Environment : : InitializeBindingHint : : Normal ) ) ;
} else {
ASM_TRY ( vm , pc , static_cast < DeclarativeEnvironment & > ( * environment ) . set_mutable_binding_direct ( vm , cache . index , value , strict = = Strict : : Yes ) ) ;
}
return static_cast < i64 > ( pc ) ;
}
template < Op : : EnvironmentMode environment_mode , Op : : BindingInitializationMode initialization_mode >
static i64 asm_dynamic_initialize_or_set_binding ( VM & vm , u32 pc , IdentifierTableIndex identifier_index , Strict strict , Value value , EnvironmentCoordinate & cache )
{
auto * environment = environment_mode = = Op : : EnvironmentMode : : Lexical
? vm . running_execution_context ( ) . lexical_environment . ptr ( )
: vm . running_execution_context ( ) . variable_environment . ptr ( ) ;
if ( auto * cached_environment = asm_get_cached_environment ( environment , cache ) ) [[likely]] {
if constexpr ( initialization_mode = = Op : : BindingInitializationMode : : Initialize ) {
ASM_TRY ( vm , pc , static_cast < DeclarativeEnvironment & > ( * cached_environment ) . initialize_binding_direct ( vm , cache . index , value , Environment : : InitializeBindingHint : : Normal ) ) ;
} else if ( initialization_mode = = Op : : BindingInitializationMode : : Set ) {
ASM_TRY ( vm , pc , static_cast < DeclarativeEnvironment & > ( * cached_environment ) . set_mutable_binding_direct ( vm , cache . index , value , strict = = Strict : : Yes ) ) ;
}
return static_cast < i64 > ( pc ) ;
}
auto reference = ASM_TRY ( vm , pc , vm . resolve_binding ( vm . get_identifier ( identifier_index ) , strict , environment ) ) ;
asm_update_environment_coordinate_cache ( environment , reference , cache ) ;
if constexpr ( initialization_mode = = Op : : BindingInitializationMode : : Initialize ) {
ASM_TRY ( vm , pc , reference . initialize_referenced_binding ( vm , value ) ) ;
} else if ( initialization_mode = = Op : : BindingInitializationMode : : Set ) {
ASM_TRY ( vm , pc , reference . put_value ( vm , value ) ) ;
}
return static_cast < i64 > ( pc ) ;
}
static ThrowCompletionOr < void > asm_create_variable ( VM & vm , Utf16FlyString const & name , Op : : EnvironmentMode mode , bool is_global , bool is_immutable , bool is_strict )
{
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 ) ) ) [[unlikely]]
2026-06-21 14:03:10 -03:00
return vm . throw_completion < InternalError > ( Utf16String : : formatted ( " Lexical environment already has binding '{}' " , name ) ) ;
2026-06-14 11:07:00 -03:00
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.
return as < GlobalEnvironment > ( vm . variable_environment ( ) ) - > create_global_var_binding ( name , false ) ;
}
struct FastPropertyNameIteratorData {
Vector < PropertyKey > properties ;
PropertyNameIterator : : FastPath fast_path { PropertyNameIterator : : FastPath : : None } ;
u32 indexed_property_count { 0 } ;
bool receiver_has_magical_length_property { false } ;
GC : : Ptr < Shape > shape ;
GC : : Ptr < PrototypeChainValidity > prototype_chain_validity ;
} ;
static bool shape_has_enumerable_string_property ( Shape const & shape )
{
bool has_enumerable_string_property = false ;
shape . for_each_property_in_insertion_order ( [ & ] ( auto const & property_key , auto const & metadata ) {
if ( property_key . is_string ( ) & & metadata . attributes . is_enumerable ( ) ) {
has_enumerable_string_property = true ;
return IterationDecision : : Break ;
}
return IterationDecision : : Continue ;
} ) ;
return has_enumerable_string_property ;
}
static bool property_name_iterator_fast_path_is_still_eligible ( Object & object , PropertyNameIterator : : FastPath fast_path , u32 indexed_property_count )
{
Object const * object_to_check = & object ;
bool is_receiver = true ;
while ( object_to_check ) {
if ( ! object_to_check - > eligible_for_own_property_enumeration_fast_path ( ) )
return false ;
if ( is_receiver ) {
if ( fast_path = = PropertyNameIterator : : FastPath : : PackedIndexed ) {
if ( object_to_check - > indexed_storage_kind ( ) ! = IndexedStorageKind : : Packed )
return false ;
if ( object_to_check - > indexed_array_like_size ( ) ! = indexed_property_count )
return false ;
} else if ( object_to_check - > indexed_array_like_size ( ) ! = 0 ) {
return false ;
}
} else if ( object_to_check - > indexed_array_like_size ( ) ! = 0 ) {
return false ;
}
object_to_check = object_to_check - > prototype ( ) ;
is_receiver = false ;
}
return true ;
}
static bool object_property_iterator_cache_matches ( Object & object , ObjectPropertyIteratorCacheData const & cache )
{
// A cache entry represents the fully flattened key snapshot for one bytecode
// site. Reusing it is only valid while the receiver still has the same local
// state and the prototype chain validity token says nothing above it changed.
if ( object . has_magical_length_property ( ) ! = cache . receiver_has_magical_length_property ( ) )
return false ;
auto & shape = object . shape ( ) ;
if ( & shape ! = cache . shape ( ) )
return false ;
if ( shape . is_dictionary ( ) & & shape . dictionary_generation ( ) ! = cache . shape_dictionary_generation ( ) )
return false ;
if ( cache . prototype_chain_validity ( ) & & ! cache . prototype_chain_validity ( ) - > is_valid ( ) )
return false ;
return property_name_iterator_fast_path_is_still_eligible ( object , cache . fast_path ( ) , cache . indexed_property_count ( ) ) ;
}
static ThrowCompletionOr < Optional < FastPropertyNameIteratorData > > asm_try_get_fast_property_name_iterator_data ( Object & object )
{
auto & vm = object . vm ( ) ;
FastPropertyNameIteratorData result { } ;
result . fast_path = PropertyNameIterator : : FastPath : : PlainNamed ;
result . receiver_has_magical_length_property = object . has_magical_length_property ( ) ;
result . shape = & object . shape ( ) ;
GC : : RootHashTable < GC : : Ref < Object > > seen_objects ;
size_t estimated_properties_count = 0 ;
bool prototype_chain_has_enumerable_named_properties = false ;
for ( auto object_to_check = GC : : Ptr { & object } ; 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 ) ;
if ( ! object_to_check - > eligible_for_own_property_enumeration_fast_path ( ) )
return Optional < FastPropertyNameIteratorData > { } ;
if ( & object = = object_to_check . ptr ( ) ) {
if ( object_to_check - > indexed_array_like_size ( ) ! = 0 ) {
if ( object_to_check - > indexed_storage_kind ( ) ! = IndexedStorageKind : : Packed )
return Optional < FastPropertyNameIteratorData > { } ;
result . fast_path = PropertyNameIterator : : FastPath : : PackedIndexed ;
result . indexed_property_count = object_to_check - > indexed_array_like_size ( ) ;
} else {
result . fast_path = PropertyNameIterator : : FastPath : : PlainNamed ;
}
} else if ( object_to_check - > indexed_array_like_size ( ) ! = 0 ) {
// The fast path only knows how to synthesize a packed indexed prefix
// for the receiver itself. As soon as indexed properties appear in
// the prototype chain, we fall back to the generic enumeration path.
return Optional < FastPropertyNameIteratorData > { } ;
} else if ( ! prototype_chain_has_enumerable_named_properties ) {
prototype_chain_has_enumerable_named_properties = shape_has_enumerable_string_property ( object_to_check - > shape ( ) ) ;
}
estimated_properties_count + = object_to_check - > shape ( ) . property_count ( ) ;
}
seen_objects . clear_with_capacity ( ) ;
if ( auto * prototype = object . shape ( ) . prototype ( ) ) {
result . prototype_chain_validity = prototype - > shape ( ) . prototype_chain_validity ( ) ;
if ( ! result . prototype_chain_validity )
return Optional < FastPropertyNameIteratorData > { } ;
}
if ( ! prototype_chain_has_enumerable_named_properties ) {
// Common case: only the receiver contributes enumerable string keys, so
// we can copy them straight from the shape without any shadowing work.
result . properties . ensure_capacity ( object . shape ( ) . property_count ( ) ) ;
object . shape ( ) . for_each_property_in_insertion_order ( [ & ] ( auto const & property_key , auto const & metadata ) {
if ( property_key . is_string ( ) & & metadata . attributes . is_enumerable ( ) )
result . properties . append ( property_key ) ;
} ) ;
return result ;
}
result . properties . ensure_capacity ( estimated_properties_count ) ;
GC : : ConservativeHashTable < PropertyKey > seen_non_enumerable_properties ;
Optional < GC : : ConservativeHashTable < PropertyKey > > seen_properties ;
auto ensure_seen_properties = [ & ] {
if ( seen_properties . has_value ( ) )
return ;
// Prototype shadowing ignores enumerability, so once we start looking
// above the receiver we need an explicit visited set for names we have
// already decided to expose from lower objects.
seen_properties . emplace ( ) ;
seen_properties - > ensure_capacity ( result . properties . size ( ) ) ;
for ( auto const & property : result . properties )
seen_properties - > set ( property ) ;
} ;
bool in_prototype_chain = false ;
for ( auto object_to_check = GC : : Ptr { & object } ; 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 ) ;
// Arrays keep a non-enumerable magical `length` property outside the shape
// table, but it still shadows enumerable `length` properties higher up the
// prototype chain during for-in.
if ( object_to_check - > has_magical_length_property ( ) )
seen_non_enumerable_properties . set ( vm . names . length ) ;
object_to_check - > shape ( ) . for_each_property_in_insertion_order ( [ & ] ( auto const & property_key , auto const & metadata ) {
if ( ! property_key . is_string ( ) )
return ;
bool enumerable = metadata . attributes . is_enumerable ( ) ;
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 )
result . properties . append ( property_key ) ;
if ( seen_properties . has_value ( ) )
seen_properties - > set ( property_key ) ;
} ) ;
in_prototype_chain = true ;
}
return result ;
}
// 14.7.5.9 EnumerateObjectProperties ( O ), https://tc39.es/ecma262/#sec-enumerate-object-properties
static ThrowCompletionOr < GC : : Ref < PropertyNameIterator > > asm_get_object_property_iterator ( VM & vm , Value value , ObjectPropertyIteratorCache * cache = nullptr )
{
// While the spec does provide an algorithm, it allows us to implement it ourselves so long as we meet the following invariants:
// 1- Returned property keys do not include keys that are Symbols
// 2- Properties of the target object may be deleted during enumeration. A property that is deleted before it is processed by the iterator's next method is ignored
// 3- If new properties are added to the target object during enumeration, the newly added properties are not guaranteed to be processed in the active enumeration
// 4- A property name will be returned by the iterator's next method at most once in any enumeration.
// 5- Enumerating the properties of the target object includes enumerating properties of its prototype, and the prototype of the prototype, and so on, recursively;
// but a property of a prototype is not processed if it has the same name as a property that has already been processed by the iterator's next method.
// 6- The values of [[Enumerable]] attributes are not considered when determining if a property of a prototype object has already been processed.
// 7- The enumerable property names of prototype objects must be obtained by invoking EnumerateObjectProperties passing the prototype object as the argument.
// 8- EnumerateObjectProperties must obtain the own property keys of the target object by calling its [[OwnPropertyKeys]] internal method.
// 9- Property attributes of the target object must be obtained by calling its [[GetOwnProperty]] internal method
// Invariant 3 effectively allows the implementation to ignore newly added keys, and we do so (similar to other implementations).
auto 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.
if ( cache & & cache - > data ) {
if ( object_property_iterator_cache_matches ( * object , * cache - > data ) ) {
if ( cache - > reusable_property_name_iterator ) {
// We keep one iterator object per bytecode site alive so hot
// loops can recycle it without allocating a new cell each time.
auto & iterator = static_cast < PropertyNameIterator & > ( * cache - > reusable_property_name_iterator ) ;
cache - > reusable_property_name_iterator = nullptr ;
iterator . reset_with_cache_data ( object , * cache - > data , cache ) ;
return iterator ;
}
return PropertyNameIterator : : create ( vm . realm ( ) , object , * cache - > data , cache ) ;
}
}
if ( auto fast_iterator_data = TRY ( asm_try_get_fast_property_name_iterator_data ( * object ) ) ; fast_iterator_data . has_value ( ) ) {
VERIFY ( fast_iterator_data - > shape ) ;
auto cache_data = vm . heap ( ) . allocate < ObjectPropertyIteratorCacheData > (
vm ,
move ( fast_iterator_data - > properties ) ,
fast_iterator_data - > fast_path ,
fast_iterator_data - > indexed_property_count ,
fast_iterator_data - > receiver_has_magical_length_property ,
* fast_iterator_data - > shape ,
fast_iterator_data - > prototype_chain_validity ) ;
if ( cache )
cache - > data = cache_data ;
if ( cache & & cache - > reusable_property_name_iterator ) {
auto & iterator = static_cast < PropertyNameIterator & > ( * cache - > reusable_property_name_iterator ) ;
cache - > reusable_property_name_iterator = nullptr ;
iterator . reset_with_cache_data ( object , cache_data , cache ) ;
return iterator ;
}
return PropertyNameIterator : : create ( vm . realm ( ) , object , cache_data , cache ) ;
}
size_t estimated_properties_count = 0 ;
GC : : RootHashTable < 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 ( ) ) ) {
seen_objects . set ( * object_to_check ) ;
estimated_properties_count + = object_to_check - > own_properties_count ( ) ;
}
seen_objects . clear_with_capacity ( ) ;
GC : : ConservativeVector < PropertyKey > properties ;
properties . ensure_capacity ( estimated_properties_count ) ;
GC : : ConservativeHashTable < PropertyKey > seen_non_enumerable_properties ;
Optional < GC : : ConservativeHashTable < PropertyKey > > seen_properties ;
auto ensure_seen_properties = [ & ] {
if ( seen_properties . has_value ( ) )
return ;
seen_properties . emplace ( ) ;
seen_properties - > ensure_capacity ( properties . size ( ) ) ;
for ( auto const & property : properties )
seen_properties - > set ( property ) ;
} ;
// Collect all keys immediately (invariant no. 5)
bool in_prototype_chain = false ;
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 > {
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 ) ;
return { } ;
} ) ) ;
in_prototype_chain = true ;
}
return PropertyNameIterator : : create ( vm . realm ( ) , object , move ( properties ) ) ;
}
extern " C " {
// Forward declarations for all functions called from assembly.
2026-06-14 09:30:56 -03:00
i64 asm_fallback_handler ( VM * , u32 pc , u8 const * instruction ) ;
i64 asm_slow_path_jump_less_than ( VM * , u32 pc , Op : : JumpLessThan const * ) ;
i64 asm_slow_path_jump_greater_than ( VM * , u32 pc , Op : : JumpGreaterThan const * ) ;
i64 asm_slow_path_jump_less_than_equals ( VM * , u32 pc , Op : : JumpLessThanEquals const * ) ;
i64 asm_slow_path_jump_greater_than_equals ( VM * , u32 pc , Op : : JumpGreaterThanEquals const * ) ;
i64 asm_slow_path_jump_loosely_equals ( VM * , u32 pc , Op : : JumpLooselyEquals const * ) ;
i64 asm_slow_path_create_private_environment ( VM * , u32 pc , Op : : CreatePrivateEnvironment const * ) ;
i64 asm_slow_path_throw_const_assignment ( VM * , u32 pc , Op : : ThrowConstAssignment const * ) ;
i64 asm_slow_path_resolve_this_binding ( VM * , u32 pc , Op : : ResolveThisBinding const * ) ;
2026-06-14 11:07:00 -03:00
# define DECLARE_CALL_BUILTIN_SLOW_PATH(name, snake_case_name, ...) \
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_call_builtin_ # # snake_case_name ( VM * , u32 pc , Op : : CallBuiltin # # name const * ) ;
2026-06-14 11:07:00 -03:00
JS_ENUMERATE_BUILTINS ( DECLARE_CALL_BUILTIN_SLOW_PATH )
# undef DECLARE_CALL_BUILTIN_SLOW_PATH
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_add ( VM * , u32 pc , Op : : Add const * ) ;
i64 asm_slow_path_sub ( VM * , u32 pc , Op : : Sub const * ) ;
i64 asm_slow_path_mul ( VM * , u32 pc , Op : : Mul const * ) ;
i64 asm_slow_path_div ( VM * , u32 pc , Op : : Div const * ) ;
i64 asm_slow_path_less_than ( VM * , u32 pc , Op : : LessThan const * ) ;
i64 asm_slow_path_less_than_equals ( VM * , u32 pc , Op : : LessThanEquals const * ) ;
i64 asm_slow_path_greater_than ( VM * , u32 pc , Op : : GreaterThan const * ) ;
i64 asm_slow_path_greater_than_equals ( VM * , u32 pc , Op : : GreaterThanEquals const * ) ;
i64 asm_slow_path_increment ( VM * , u32 pc , Op : : Increment const * ) ;
i64 asm_slow_path_decrement ( VM * , u32 pc , Op : : Decrement const * ) ;
i64 asm_slow_path_jump_loosely_inequals ( VM * , u32 pc , Op : : JumpLooselyInequals const * ) ;
i64 asm_slow_path_jump_strictly_equals ( VM * , u32 pc , Op : : JumpStrictlyEquals const * ) ;
i64 asm_slow_path_jump_strictly_inequals ( VM * , u32 pc , Op : : JumpStrictlyInequals const * ) ;
i64 asm_slow_path_get_initialized_binding ( VM * , u32 pc , Op : : GetInitializedBinding const * ) ;
i64 asm_slow_path_dynamic_get_initialized_binding ( VM * , u32 pc , Op : : DynamicGetInitializedBinding const * ) ;
i64 asm_slow_path_get_callee_and_this ( VM * , u32 pc , Op : : GetCalleeAndThisFromEnvironment const * ) ;
i64 asm_slow_path_dynamic_get_callee_and_this ( VM * , u32 pc , Op : : DynamicGetCalleeAndThisFromEnvironment const * ) ;
i64 asm_slow_path_postfix_increment ( VM * , u32 pc , Op : : PostfixIncrement const * ) ;
i64 asm_slow_path_get_by_id ( VM * , u32 pc , Op : : GetById const * ) ;
i64 asm_slow_path_get_by_id_with_this ( VM * , u32 pc , Op : : GetByIdWithThis const * ) ;
i64 asm_slow_path_put_by_id ( VM * , u32 pc , Op : : PutById const * ) ;
i64 asm_slow_path_put_by_id_with_this ( VM * , u32 pc , Op : : PutByIdWithThis const * ) ;
i64 asm_slow_path_get_by_value ( VM * , u32 pc , Op : : GetByValue const * ) ;
i64 asm_slow_path_get_by_value_with_this ( VM * , u32 pc , Op : : GetByValueWithThis const * ) ;
i64 asm_slow_path_get_length ( VM * , u32 pc , Op : : GetLength const * ) ;
i64 asm_slow_path_get_length_with_this ( VM * , u32 pc , Op : : GetLengthWithThis const * ) ;
i64 asm_slow_path_get_method ( VM * , u32 pc , Op : : GetMethod const * ) ;
i64 asm_slow_path_get_iterator ( VM * , u32 pc , Op : : GetIterator const * ) ;
i64 asm_slow_path_get_import_meta ( VM * , u32 pc , Op : : GetImportMeta const * ) ;
i64 asm_slow_path_get_new_target ( VM * , u32 pc , Op : : GetNewTarget const * ) ;
i64 asm_slow_path_get_super_constructor ( VM * , u32 pc , Op : : GetSuperConstructor const * ) ;
i64 asm_slow_path_get_global ( VM * , u32 pc , Op : : GetGlobal const * ) ;
i64 asm_slow_path_set_global ( VM * , u32 pc , Op : : SetGlobal const * ) ;
i64 asm_slow_path_concat_string ( VM * , u32 pc , Op : : ConcatString const * ) ;
i64 asm_slow_path_copy_object_excluding_properties ( VM * , u32 pc , Op : : CopyObjectExcludingProperties const * ) ;
i64 asm_slow_path_exp ( VM * , u32 pc , Op : : Exp const * ) ;
i64 asm_slow_path_import_call ( VM * , u32 pc , Op : : ImportCall const * ) ;
i64 asm_slow_path_new_class ( VM * , u32 pc , Op : : NewClass const * ) ;
i64 asm_slow_path_call ( VM * , u32 pc , Op : : Call const * ) ;
i64 asm_slow_path_call_direct_eval ( VM * , u32 pc , Op : : CallDirectEval const * ) ;
i64 asm_slow_path_call_with_argument_array ( VM * , u32 pc , Op : : CallWithArgumentArray const * ) ;
i64 asm_slow_path_call_direct_eval_with_argument_array ( VM * , u32 pc , Op : : CallDirectEvalWithArgumentArray const * ) ;
i64 asm_slow_path_get_object_property_iterator ( VM * , u32 pc , Op : : GetObjectPropertyIterator const * ) ;
i64 asm_slow_path_object_property_iterator_next ( VM * , u32 pc , Op : : ObjectPropertyIteratorNext const * ) ;
i64 asm_slow_path_iterator_close ( VM * , u32 pc , Op : : IteratorClose const * ) ;
i64 asm_slow_path_iterator_next ( VM * , u32 pc , Op : : IteratorNext const * ) ;
i64 asm_slow_path_iterator_next_unpack ( VM * , u32 pc , Op : : IteratorNextUnpack const * ) ;
i64 asm_slow_path_iterator_to_array ( VM * , u32 pc , Op : : IteratorToArray const * ) ;
i64 asm_slow_path_call_construct ( VM * , u32 pc , Op : : CallConstruct const * ) ;
i64 asm_slow_path_call_construct_with_argument_array ( VM * , u32 pc , Op : : CallConstructWithArgumentArray const * ) ;
i64 asm_slow_path_super_call_with_argument_array ( VM * , u32 pc , Op : : SuperCallWithArgumentArray const * ) ;
i64 asm_slow_path_new_object ( VM * , u32 pc , Op : : NewObject const * ) ;
i64 asm_slow_path_new_object_with_no_prototype ( VM * , u32 pc , Op : : NewObjectWithNoPrototype const * ) ;
i64 asm_slow_path_cache_object_shape ( VM * , u32 pc , Op : : CacheObjectShape const * ) ;
i64 asm_slow_path_init_object_literal_property ( VM * , u32 pc , Op : : InitObjectLiteralProperty const * ) ;
i64 asm_slow_path_new_array ( VM * , u32 pc , Op : : NewArray const * ) ;
i64 asm_slow_path_new_primitive_array ( VM * , u32 pc , Op : : NewPrimitiveArray const * ) ;
i64 asm_slow_path_new_regexp ( VM * , u32 pc , Op : : NewRegExp const * ) ;
i64 asm_slow_path_new_reference_error ( VM * , u32 pc , Op : : NewReferenceError const * ) ;
i64 asm_slow_path_new_type_error ( VM * , u32 pc , Op : : NewTypeError const * ) ;
i64 asm_slow_path_bitwise_xor ( VM * , u32 pc , Op : : BitwiseXor const * ) ;
i64 asm_slow_path_bitwise_and ( VM * , u32 pc , Op : : BitwiseAnd const * ) ;
i64 asm_slow_path_bitwise_or ( VM * , u32 pc , Op : : BitwiseOr const * ) ;
i64 asm_slow_path_left_shift ( VM * , u32 pc , Op : : LeftShift const * ) ;
i64 asm_slow_path_right_shift ( VM * , u32 pc , Op : : RightShift const * ) ;
i64 asm_slow_path_unsigned_right_shift ( VM * , u32 pc , Op : : UnsignedRightShift const * ) ;
i64 asm_slow_path_mod ( VM * , u32 pc , Op : : Mod const * ) ;
i64 asm_slow_path_strictly_equals ( VM * , u32 pc , Op : : StrictlyEquals const * ) ;
i64 asm_slow_path_strictly_inequals ( VM * , u32 pc , Op : : StrictlyInequals const * ) ;
i64 asm_slow_path_loosely_equals ( VM * , u32 pc , Op : : LooselyEquals const * ) ;
i64 asm_slow_path_loosely_inequals ( VM * , u32 pc , Op : : LooselyInequals const * ) ;
i64 asm_slow_path_unary_minus ( VM * , u32 pc , Op : : UnaryMinus const * ) ;
i64 asm_slow_path_to_string ( VM * , u32 pc , Op : : ToString const * ) ;
i64 asm_slow_path_to_primitive_with_string_hint ( VM * , u32 pc , Op : : ToPrimitiveWithStringHint const * ) ;
i64 asm_slow_path_to_object ( VM * , u32 pc , Op : : ToObject const * ) ;
i64 asm_slow_path_to_length ( VM * , u32 pc , Op : : ToLength const * ) ;
i64 asm_slow_path_typeof ( VM * , u32 pc , Op : : Typeof const * ) ;
i64 asm_slow_path_postfix_decrement ( VM * , u32 pc , Op : : PostfixDecrement const * ) ;
i64 asm_slow_path_to_int32 ( VM * , u32 pc , Op : : ToInt32 const * ) ;
i64 asm_slow_path_put_by_value ( VM * , u32 pc , Op : : PutByValue const * ) ;
i64 asm_slow_path_put_by_value_with_this ( VM * , u32 pc , Op : : PutByValueWithThis const * ) ;
i64 asm_slow_path_put_by_spread ( VM * , u32 pc , Op : : PutBySpread const * ) ;
i64 asm_slow_path_get_binding ( VM * , u32 pc , Op : : GetBinding const * ) ;
i64 asm_slow_path_dynamic_get_binding ( VM * , u32 pc , Op : : DynamicGetBinding const * ) ;
i64 asm_slow_path_initialize_lexical_binding ( VM * , u32 pc , Op : : InitializeLexicalBinding const * ) ;
i64 asm_slow_path_dynamic_initialize_lexical_binding ( VM * , u32 pc , Op : : DynamicInitializeLexicalBinding const * ) ;
i64 asm_slow_path_initialize_variable_binding ( VM * , u32 pc , Op : : InitializeVariableBinding const * ) ;
i64 asm_slow_path_dynamic_initialize_variable_binding ( VM * , u32 pc , Op : : DynamicInitializeVariableBinding const * ) ;
i64 asm_slow_path_set_lexical_binding ( VM * , u32 pc , Op : : SetLexicalBinding const * ) ;
i64 asm_slow_path_dynamic_set_lexical_binding ( VM * , u32 pc , Op : : DynamicSetLexicalBinding const * ) ;
i64 asm_slow_path_set_variable_binding ( VM * , u32 pc , Op : : SetVariableBinding const * ) ;
i64 asm_slow_path_dynamic_set_variable_binding ( VM * , u32 pc , Op : : DynamicSetVariableBinding const * ) ;
i64 asm_slow_path_resolve_binding ( VM * , u32 pc , Op : : ResolveBinding const * ) ;
i64 asm_slow_path_resolve_super_base ( VM * , u32 pc , Op : : ResolveSuperBase const * ) ;
i64 asm_slow_path_set_resolved_binding ( VM * , u32 pc , Op : : SetResolvedBinding const * ) ;
i64 asm_slow_path_typeof_binding ( VM * , u32 pc , Op : : TypeofBinding const * ) ;
i64 asm_slow_path_dynamic_typeof_binding ( VM * , u32 pc , Op : : DynamicTypeofBinding const * ) ;
i64 asm_slow_path_has_private_id ( VM * , u32 pc , Op : : HasPrivateId const * ) ;
i64 asm_slow_path_set_function_name ( VM * , u32 pc , Op : : SetFunctionName const * ) ;
i64 asm_slow_path_new_array_with_length ( VM * , u32 pc , Op : : NewArrayWithLength const * ) ;
i64 asm_slow_path_array_append ( VM * , u32 pc , Op : : ArrayAppend const * ) ;
i64 asm_slow_path_create_variable ( VM * , u32 pc , Op : : CreateVariable const * ) ;
i64 asm_slow_path_enter_object_environment ( VM * , u32 pc , Op : : EnterObjectEnvironment const * ) ;
i64 asm_slow_path_bitwise_not ( VM * , u32 pc , Op : : BitwiseNot const * ) ;
i64 asm_slow_path_unary_plus ( VM * , u32 pc , Op : : UnaryPlus const * ) ;
i64 asm_slow_path_is_constructor ( VM * , u32 pc , Op : : IsConstructor const * ) ;
i64 asm_slow_path_add_private_name ( VM * , u32 pc , Op : : AddPrivateName const * ) ;
i64 asm_slow_path_create_async_from_sync_iterator ( VM * , u32 pc , Op : : CreateAsyncFromSyncIterator const * ) ;
i64 asm_slow_path_create_data_property_or_throw ( VM * , u32 pc , Op : : CreateDataPropertyOrThrow const * ) ;
i64 asm_slow_path_create_immutable_binding ( VM * , u32 pc , Op : : CreateImmutableBinding const * ) ;
i64 asm_slow_path_create_mutable_binding ( VM * , u32 pc , Op : : CreateMutableBinding const * ) ;
i64 asm_slow_path_create_rest_params ( VM * , u32 pc , Op : : CreateRestParams const * ) ;
i64 asm_slow_path_create_arguments ( VM * , u32 pc , Op : : CreateArguments const * ) ;
i64 asm_slow_path_await ( VM * , u32 pc , Op : : Await const * ) ;
i64 asm_slow_path_create_lexical_environment ( VM * , u32 pc , Op : : CreateLexicalEnvironment const * ) ;
i64 asm_slow_path_create_variable_environment ( VM * , u32 pc , Op : : CreateVariableEnvironment const * ) ;
i64 asm_slow_path_delete_by_id ( VM * , u32 pc , Op : : DeleteById const * ) ;
i64 asm_slow_path_delete_by_value ( VM * , u32 pc , Op : : DeleteByValue const * ) ;
i64 asm_slow_path_delete_variable ( VM * , u32 pc , Op : : DeleteVariable const * ) ;
i64 asm_slow_path_get_completion_fields ( VM * , u32 pc , Op : : GetCompletionFields const * ) ;
i64 asm_slow_path_set_completion_type ( VM * , u32 pc , Op : : SetCompletionType const * ) ;
i64 asm_slow_path_get_template_object ( VM * , u32 pc , Op : : GetTemplateObject const * ) ;
i64 asm_slow_path_new_function ( VM * , u32 pc , Op : : NewFunction const * ) ;
i64 asm_slow_path_throw ( VM * , u32 pc , Op : : Throw const * ) ;
i64 asm_slow_path_throw_if_tdz ( VM * , u32 pc , Op : : ThrowIfTDZ const * ) ;
i64 asm_slow_path_throw_if_not_object ( VM * , u32 pc , Op : : ThrowIfNotObject const * ) ;
i64 asm_slow_path_throw_if_nullish ( VM * , u32 pc , Op : : ThrowIfNullish const * ) ;
i64 asm_slow_path_yield ( VM * , u32 pc , Op : : Yield const * ) ;
i64 asm_slow_path_yield_iterator_result ( VM * , u32 pc , Op : : YieldIteratorResult const * ) ;
i64 asm_slow_path_instance_of ( VM * , u32 pc , Op : : InstanceOf const * ) ;
i64 asm_slow_path_in ( VM * , u32 pc , Op : : In const * ) ;
i64 asm_slow_path_get_private_by_id ( VM * , u32 pc , Op : : GetPrivateById const * ) ;
i64 asm_slow_path_put_private_by_id ( VM * , u32 pc , Op : : PutPrivateById const * ) ;
i64 asm_try_get_global_env_binding ( VM * , u32 pc , Op : : GetGlobal const * ) ;
i64 asm_try_set_global_env_binding ( VM * , u32 pc , Op : : SetGlobal const * ) ;
i64 asm_try_put_by_value_holey_array ( VM * , u32 pc , Op : : PutByValue const * ) ;
2026-06-14 11:07:00 -03:00
u64 asm_helper_to_boolean ( u64 encoded_value ) ;
u64 asm_helper_math_exp ( u64 encoded_value ) ;
u64 asm_helper_empty_string ( u64 ) ;
u64 asm_helper_single_ascii_character_string ( u64 encoded_value ) ;
u64 asm_helper_single_utf16_code_unit_string ( u64 encoded_value ) ;
i64 asm_helper_handle_raw_native_exception ( u64 encoded_exception ) ;
2026-06-14 09:30:56 -03:00
i64 asm_try_inline_call ( VM * , u32 pc , Op : : Call const * ) ;
i64 asm_try_put_by_id_cache ( VM * , u32 pc , Op : : PutById const * ) ;
i64 asm_try_get_by_id_cache ( VM * , u32 pc , Op : : GetById const * ) ;
i64 asm_try_get_by_value_typed_array ( VM * , u32 pc , Op : : GetByValue const * ) ;
i64 asm_try_put_by_value_typed_array ( VM * , u32 pc , Op : : PutByValue const * ) ;
2026-06-14 11:07:00 -03:00
// ===== Fallback handler for invalid dispatch table entries =====
// NB: Every bytecode opcode has a DSL handler, so this should never run.
2026-06-14 09:30:56 -03:00
i64 asm_fallback_handler ( VM * , u32 , u8 const * )
2026-06-14 11:07:00 -03:00
{
VERIFY_NOT_REACHED ( ) ;
}
// ===== Specific slow paths for asm-optimized instructions =====
// These are called from asm handlers when the fast path fails.
2026-06-14 09:30:56 -03:00
// Convention: i64 func(VM*, u32 pc, Op::Foo const* instruction)
2026-06-14 11:07:00 -03:00
// Returns >= 0: new pc
// Returns < 0: exit
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_add ( VM * vm , u32 pc , Op : : Add const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , add ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : Add ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_sub ( VM * vm , u32 pc , Op : : Sub const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , sub ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : Sub ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_mul ( VM * vm , u32 pc , Op : : Mul const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , mul ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : Mul ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_div ( VM * vm , u32 pc , Op : : Div const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , div ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : Div ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_less_than ( VM * vm , u32 pc , Op : : LessThan const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , Value { ASM_TRY ( * vm , pc , less_than ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) } ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : LessThan ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_less_than_equals ( VM * vm , u32 pc , Op : : LessThanEquals const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , Value { ASM_TRY ( * vm , pc , less_than_equals ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) } ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : LessThanEquals ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_greater_than ( VM * vm , u32 pc , Op : : GreaterThan const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , Value { ASM_TRY ( * vm , pc , greater_than ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) } ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GreaterThan ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_greater_than_equals ( VM * vm , u32 pc , Op : : GreaterThanEquals const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , Value { ASM_TRY ( * vm , pc , greater_than_equals ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) } ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GreaterThanEquals ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_increment ( VM * vm , u32 pc , Op : : Increment const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto old_value = ASM_TRY ( * vm , pc , vm - > get ( instruction - > dst ( ) ) . to_numeric ( * vm ) ) ;
2026-06-14 11:07:00 -03:00
if ( old_value . is_number ( ) )
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , Value ( old_value . as_double ( ) + 1 ) ) ;
2026-06-14 11:07:00 -03:00
else
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , BigInt : : create ( * vm , old_value . as_bigint ( ) . big_integer ( ) . plus ( Crypto : : SignedBigInteger { 1 } ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : Increment ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_decrement ( VM * vm , u32 pc , Op : : Decrement const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto old_value = ASM_TRY ( * vm , pc , vm - > get ( instruction - > dst ( ) ) . to_numeric ( * vm ) ) ;
2026-06-14 11:07:00 -03:00
if ( old_value . is_number ( ) )
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , Value ( old_value . as_double ( ) - 1 ) ) ;
2026-06-14 11:07:00 -03:00
else
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , BigInt : : create ( * vm , old_value . as_bigint ( ) . big_integer ( ) . minus ( Crypto : : SignedBigInteger { 1 } ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : Decrement ) ) ;
}
// Comparison jump slow paths return one of two target PCs.
2026-06-14 09:30:56 -03:00
# define DEFINE_JUMP_COMPARISON_SLOW_PATH(snake_name, op_name, compare_call) \
i64 asm_slow_path_jump_ # # snake_name ( VM * vm , u32 pc , Op : : Jump # # op_name const * instruction ) \
{ \
auto lhs = vm - > get ( instruction - > lhs ( ) ) ; \
auto rhs = vm - > get ( instruction - > rhs ( ) ) ; \
if ( ASM_TRY ( * vm , pc , compare_call ) ) \
return static_cast < i64 > ( instruction - > true_target ( ) . address ( ) ) ; \
return static_cast < i64 > ( instruction - > false_target ( ) . address ( ) ) ; \
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:46:23 -03:00
DEFINE_JUMP_COMPARISON_SLOW_PATH ( less_than , LessThan , less_than ( * vm , lhs , rhs ) )
DEFINE_JUMP_COMPARISON_SLOW_PATH ( greater_than , GreaterThan , greater_than ( * vm , lhs , rhs ) )
DEFINE_JUMP_COMPARISON_SLOW_PATH ( less_than_equals , LessThanEquals , less_than_equals ( * vm , lhs , rhs ) )
DEFINE_JUMP_COMPARISON_SLOW_PATH ( greater_than_equals , GreaterThanEquals , greater_than_equals ( * vm , lhs , rhs ) )
DEFINE_JUMP_COMPARISON_SLOW_PATH ( loosely_equals , LooselyEquals , is_loosely_equal ( * vm , lhs , rhs ) )
2026-06-14 11:07:00 -03:00
# undef DEFINE_JUMP_COMPARISON_SLOW_PATH
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_jump_loosely_inequals ( VM * vm , u32 pc , Op : : JumpLooselyInequals const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto lhs = vm - > get ( instruction - > lhs ( ) ) ;
auto rhs = vm - > get ( instruction - > rhs ( ) ) ;
2026-06-14 09:46:23 -03:00
if ( ! ASM_TRY ( * vm , pc , is_loosely_equal ( * vm , lhs , rhs ) ) )
2026-06-14 09:30:56 -03:00
return static_cast < i64 > ( instruction - > true_target ( ) . address ( ) ) ;
return static_cast < i64 > ( instruction - > false_target ( ) . address ( ) ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_jump_strictly_equals ( VM * vm , [[maybe_unused]] u32 pc , Op : : JumpStrictlyEquals const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto lhs = vm - > get ( instruction - > lhs ( ) ) ;
auto rhs = vm - > get ( instruction - > rhs ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( is_strictly_equal ( lhs , rhs ) )
2026-06-14 09:30:56 -03:00
return static_cast < i64 > ( instruction - > true_target ( ) . address ( ) ) ;
return static_cast < i64 > ( instruction - > false_target ( ) . address ( ) ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_jump_strictly_inequals ( VM * vm , [[maybe_unused]] u32 pc , Op : : JumpStrictlyInequals const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto lhs = vm - > get ( instruction - > lhs ( ) ) ;
auto rhs = vm - > get ( instruction - > rhs ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( ! is_strictly_equal ( lhs , rhs ) )
2026-06-14 09:30:56 -03:00
return static_cast < i64 > ( instruction - > true_target ( ) . address ( ) ) ;
return static_cast < i64 > ( instruction - > false_target ( ) . address ( ) ) ;
2026-06-14 11:07:00 -03:00
}
// ===== Dedicated slow paths for hot instructions =====
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_initialized_binding ( VM * vm , u32 pc , Op : : GetInitializedBinding const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto next_pc = asm_get_binding < AsmBindingIsKnownToBeInitialized : : Yes > ( * vm , pc , instruction - > dst ( ) , instruction - > cache ( ) ) ;
return advance_or_continue < Op : : GetInitializedBinding > ( pc , next_pc ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_dynamic_get_initialized_binding ( VM * vm , u32 pc , Op : : DynamicGetInitializedBinding const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & cache = vm - > current_executable ( ) . environment_coordinate_caches [ instruction - > cache ( ) ] ;
auto next_pc = asm_dynamic_get_binding < AsmBindingIsKnownToBeInitialized : : Yes > ( * vm , pc , instruction - > dst ( ) , instruction - > identifier ( ) , instruction - > strict ( ) , cache ) ;
return advance_or_continue < Op : : DynamicGetInitializedBinding > ( pc , next_pc ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_callee_and_this ( VM * vm , u32 pc , Op : : GetCalleeAndThisFromEnvironment const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto const & cache = instruction - > cache ( ) ;
2026-06-14 11:07:00 -03:00
VERIFY ( cache . is_valid ( ) ) ;
auto const * environment = vm - > running_execution_context ( ) . lexical_environment . ptr ( ) ;
for ( size_t i = 0 ; i < cache . hops ; + + i )
environment = environment - > outer_environment ( ) ;
auto callee = ASM_TRY ( * vm , pc , static_cast < DeclarativeEnvironment const & > ( * environment ) . get_binding_value_direct ( * vm , cache . index ) ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > callee ( ) , callee ) ;
2026-06-14 11:07:00 -03:00
auto this_value = js_undefined ( ) ;
if ( auto base_object = environment - > with_base_object ( ) ) [[unlikely]]
this_value = base_object ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > this_value ( ) , this_value ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetCalleeAndThisFromEnvironment ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_dynamic_get_callee_and_this ( VM * vm , u32 pc , Op : : DynamicGetCalleeAndThisFromEnvironment const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & cache = vm - > current_executable ( ) . environment_coordinate_caches [ instruction - > cache ( ) ] ;
auto next_pc = asm_dynamic_get_callee_and_this_from_environment ( * vm , pc , instruction - > callee ( ) , instruction - > this_value ( ) , instruction - > identifier ( ) , instruction - > strict ( ) , cache ) ;
return advance_or_continue < Op : : DynamicGetCalleeAndThisFromEnvironment > ( pc , next_pc ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_postfix_increment ( VM * vm , u32 pc , Op : : PostfixIncrement const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto old_value = ASM_TRY ( * vm , pc , vm - > get ( instruction - > src ( ) ) . to_numeric ( * vm ) ) ;
vm - > set ( instruction - > dst ( ) , old_value ) ;
2026-06-14 11:07:00 -03:00
if ( old_value . is_number ( ) )
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > src ( ) , Value ( old_value . as_double ( ) + 1 ) ) ;
2026-06-14 11:07:00 -03:00
else
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > src ( ) , BigInt : : create ( * vm , old_value . as_bigint ( ) . big_integer ( ) . plus ( Crypto : : SignedBigInteger { 1 } ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : PostfixIncrement ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_by_id ( VM * vm , u32 pc , Op : : GetById const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto base_value = vm - > get ( instruction - > base ( ) ) ;
auto & cache = vm - > current_executable ( ) . property_lookup_caches [ instruction - > cache ( ) ] ;
auto value = ASM_TRY ( * vm , pc , get_by_id < GetByIdMode : : Normal > ( * vm , [ & ] { return vm - > get_identifier ( instruction - > base_identifier ( ) ) ; } , [ & ] - > PropertyKey const & { return vm - > get_property_key ( instruction - > property ( ) ) ; } , base_value , base_value , cache ) ) ;
vm - > set ( instruction - > dst ( ) , value ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetById ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_by_id_with_this ( VM * vm , u32 pc , Op : : GetByIdWithThis const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto base_value = vm - > get ( instruction - > base ( ) ) ;
auto this_value = vm - > get ( instruction - > this_value ( ) ) ;
auto & cache = vm - > current_executable ( ) . property_lookup_caches [ instruction - > cache ( ) ] ;
auto value = ASM_TRY ( * vm , pc , get_by_id < GetByIdMode : : Normal > ( * vm , [ ] { return Optional < Utf16FlyString const & > { } ; } , [ & ] - > PropertyKey const & { return vm - > get_property_key ( instruction - > property ( ) ) ; } , base_value , this_value , cache ) ) ;
vm - > set ( instruction - > dst ( ) , value ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetByIdWithThis ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_put_by_id ( VM * vm , u32 pc , Op : : PutById const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto value = vm - > get ( instruction - > src ( ) ) ;
auto base = vm - > get ( instruction - > base ( ) ) ;
2026-06-14 11:07:00 -03:00
Optional < Utf16FlyString const & > base_identifier ;
2026-06-14 09:30:56 -03:00
if ( instruction - > base_identifier ( ) . has_value ( ) )
base_identifier = vm - > get_identifier ( instruction - > base_identifier ( ) . value ( ) ) ;
auto const & property_key = vm - > get_property_key ( instruction - > property ( ) ) ;
auto & cache = vm - > current_executable ( ) . property_lookup_caches [ instruction - > cache ( ) ] ;
ASM_TRY ( * vm , pc , put_by_property_key ( * vm , base , base , value , base_identifier , property_key , instruction - > kind ( ) , instruction - > strict ( ) , & cache ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : PutById ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_put_by_id_with_this ( VM * vm , u32 pc , Op : : PutByIdWithThis const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto value = vm - > get ( instruction - > src ( ) ) ;
auto base = vm - > get ( instruction - > base ( ) ) ;
auto const & name = vm - > get_property_key ( instruction - > property ( ) ) ;
auto & cache = vm - > current_executable ( ) . property_lookup_caches [ instruction - > cache ( ) ] ;
ASM_TRY ( * vm , pc , put_by_property_key ( * vm , base , vm - > get ( instruction - > this_value ( ) ) , value , { } , name , instruction - > kind ( ) , instruction - > strict ( ) , & cache ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : PutByIdWithThis ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_by_value ( VM * vm , u32 pc , Op : : GetByValue const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto base_value = vm - > get ( instruction - > base ( ) ) ;
auto property_key_value = vm - > get ( instruction - > property ( ) ) ;
2026-06-14 11:07:00 -03:00
auto object = ASM_TRY ( * vm , pc , base_object_for_get ( * vm , base_value , [ & ] ( ) - > Optional < Utf16FlyString const & > {
2026-06-14 09:30:56 -03:00
if ( instruction - > base_identifier ( ) . has_value ( ) )
return vm - > get_identifier ( instruction - > base_identifier ( ) . value ( ) ) ;
2026-06-14 11:07:00 -03:00
return { } ; } , [ & ] { return property_key_value ; } ) ) ;
auto property_key = ASM_TRY ( * vm , pc , property_key_value . to_property_key ( * vm ) ) ;
if ( base_value . is_string ( ) ) {
auto string_value = ASM_TRY ( * vm , pc , base_value . as_string ( ) . get ( * vm , property_key ) ) ;
if ( string_value . has_value ( ) ) {
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , * string_value ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetByValue ) ) ;
}
}
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , object - > internal_get ( property_key , base_value ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetByValue ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_by_value_with_this ( VM * vm , u32 pc , Op : : GetByValueWithThis const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto property_key_value = vm - > get ( instruction - > property ( ) ) ;
auto object = ASM_TRY ( * vm , pc , vm - > get ( instruction - > base ( ) ) . to_object ( * vm ) ) ;
2026-06-14 11:07:00 -03:00
auto property_key = ASM_TRY ( * vm , pc , property_key_value . to_property_key ( * vm ) ) ;
2026-06-14 09:30:56 -03:00
auto value = ASM_TRY ( * vm , pc , object - > internal_get ( property_key , vm - > get ( instruction - > this_value ( ) ) ) ) ;
vm - > set ( instruction - > dst ( ) , value ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetByValueWithThis ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_length ( VM * vm , u32 pc , Op : : GetLength const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto base_value = vm - > get ( instruction - > base ( ) ) ;
2026-06-14 11:07:00 -03:00
auto & executable = vm - > current_executable ( ) ;
2026-06-14 09:30:56 -03:00
auto & cache = executable . property_lookup_caches [ instruction - > cache ( ) ] ;
auto value = ASM_TRY ( * vm , pc , get_by_id < GetByIdMode : : Length > ( * vm , [ & ] { return vm - > get_identifier ( instruction - > base_identifier ( ) ) ; } , [ & ] - > PropertyKey const & { return executable . get_property_key ( * executable . length_identifier ) ; } , base_value , base_value , cache ) ) ;
vm - > set ( instruction - > dst ( ) , value ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetLength ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_length_with_this ( VM * vm , u32 pc , Op : : GetLengthWithThis const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto base_value = vm - > get ( instruction - > base ( ) ) ;
auto this_value = vm - > get ( instruction - > this_value ( ) ) ;
2026-06-14 11:07:00 -03:00
auto & executable = vm - > current_executable ( ) ;
2026-06-14 09:30:56 -03:00
auto & cache = executable . property_lookup_caches [ instruction - > cache ( ) ] ;
2026-06-14 11:07:00 -03:00
auto value = ASM_TRY ( * vm , pc , get_by_id < GetByIdMode : : Length > ( * vm , [ ] { return Optional < Utf16FlyString const & > { } ; } , [ & ] - > PropertyKey const & { return executable . get_property_key ( * executable . length_identifier ) ; } , base_value , this_value , cache ) ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , value ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetLengthWithThis ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_method ( VM * vm , u32 pc , Op : : GetMethod const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto const & property_key = vm - > get_property_key ( instruction - > property ( ) ) ;
auto method = ASM_TRY ( * vm , pc , vm - > get ( instruction - > object ( ) ) . get_method ( * vm , property_key ) ) ;
vm - > set ( instruction - > dst ( ) , method ? : js_undefined ( ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetMethod ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_iterator ( VM * vm , u32 pc , Op : : GetIterator const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto iterator_record = ASM_TRY ( * vm , pc , get_iterator_impl ( * vm , vm - > get ( instruction - > iterable ( ) ) , instruction - > hint ( ) ) ) ;
vm - > set ( instruction - > dst_iterator_object ( ) , iterator_record . iterator ) ;
vm - > set ( instruction - > dst_iterator_next ( ) , iterator_record . next_method ) ;
vm - > set ( instruction - > dst_iterator_done ( ) , Value ( iterator_record . done ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetIterator ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_import_meta ( VM * vm , u32 pc , Op : : GetImportMeta const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , vm - > get_import_meta ( ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetImportMeta ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_new_target ( VM * vm , u32 pc , Op : : GetNewTarget const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , vm - > get_new_target ( ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetNewTarget ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_super_constructor ( VM * vm , u32 pc , Op : : GetSuperConstructor const * instruction )
2026-06-14 11:07:00 -03:00
{
auto * super_constructor = get_super_constructor ( * vm ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , super_constructor ? Value ( super_constructor ) : js_null ( ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetSuperConstructor ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_try_get_global_env_binding ( VM * vm , u32 , Op : : GetGlobal const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & cache = vm - > current_executable ( ) . global_variable_caches [ instruction - > cache ( ) ] ;
2026-06-14 11:07:00 -03:00
if ( ! cache . has_environment_binding_index ) [[unlikely]]
return 1 ;
auto & current_vm = * vm ;
ThrowCompletionOr < Value > result = js_undefined ( ) ;
if ( cache . in_module_environment ) {
auto module = current_vm . running_execution_context ( ) . script_or_module . get_pointer < GC : : Ref < Module > > ( ) ;
if ( ! module ) [[unlikely]]
return 1 ;
result = ( * module ) - > environment ( ) - > get_binding_value_direct ( current_vm , cache . environment_binding_index ) ;
} else {
result = vm - > global_declarative_environment ( ) . get_binding_value_direct ( current_vm , cache . environment_binding_index ) ;
}
if ( result . is_error ( ) ) [[unlikely]]
return 1 ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , result . value ( ) ) ;
2026-06-14 11:07:00 -03:00
return 0 ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_global ( VM * vm , u32 pc , Op : : GetGlobal const * instruction )
2026-06-14 11:07:00 -03:00
{
auto & binding_object = vm - > global_object ( ) ;
auto & declarative_record = vm - > global_declarative_environment ( ) ;
2026-06-14 09:30:56 -03:00
auto & cache = vm - > current_executable ( ) . global_variable_caches [ instruction - > cache ( ) ] ;
2026-06-14 11:07:00 -03:00
auto & shape = binding_object . shape ( ) ;
if ( cache . environment_serial_number = = declarative_record . environment_serial_number ( ) ) {
auto * entry = cache . first_entry ( ) ;
if ( entry & & & shape = = entry - > shape & & ( ! shape . is_dictionary ( ) | | shape . dictionary_generation ( ) = = entry - > shape_dictionary_generation ) ) {
auto value = binding_object . get_direct ( entry - > property_offset ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , get_cached_property_value ( * vm , value , & binding_object ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetGlobal ) ) ;
}
if ( cache . has_environment_binding_index ) {
Value value ;
if ( cache . in_module_environment ) {
auto module = vm - > running_execution_context ( ) . script_or_module . get_pointer < GC : : Ref < Module > > ( ) ;
value = ASM_TRY ( * vm , pc , ( * module ) - > environment ( ) - > get_binding_value_direct ( * vm , cache . environment_binding_index ) ) ;
} else {
value = ASM_TRY ( * vm , pc , declarative_record . get_binding_value_direct ( * vm , cache . environment_binding_index ) ) ;
}
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , value ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetGlobal ) ) ;
}
}
cache . environment_serial_number = declarative_record . environment_serial_number ( ) ;
2026-06-14 09:30:56 -03:00
auto & identifier = vm - > get_identifier ( instruction - > identifier ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( auto * module = vm - > running_execution_context ( ) . script_or_module . get_pointer < GC : : Ref < Module > > ( ) ) {
auto & module_environment = * ( * module ) - > environment ( ) ;
Optional < size_t > index ;
if ( ASM_TRY ( * vm , pc , 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 ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , module_environment . get_binding_value_direct ( * vm , index . value ( ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetGlobal ) ) ;
}
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , module_environment . get_binding_value ( * vm , identifier , true ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetGlobal ) ) ;
}
}
Optional < size_t > offset ;
if ( ASM_TRY ( * vm , pc , 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 ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , declarative_record . get_binding_value ( * vm , identifier , instruction - > strict ( ) = = Strict : : Yes ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetGlobal ) ) ;
}
if ( ASM_TRY ( * vm , pc , binding_object . has_property ( identifier ) ) ) [[likely]] {
CacheableGetPropertyMetadata cacheable_metadata ;
auto value = ASM_TRY ( * vm , pc , binding_object . internal_get ( identifier , & binding_object , & cacheable_metadata ) ) ;
if ( cacheable_metadata . type = = CacheableGetPropertyMetadata : : Type : : GetOwnProperty ) {
cache . update ( PropertyLookupCache : : Entry : : Type : : GetOwnProperty , [ & ] ( auto & entry ) {
entry . shape = shape ;
entry . property_offset = cacheable_metadata . property_offset . value ( ) ;
if ( shape . is_dictionary ( ) )
entry . shape_dictionary_generation = shape . dictionary_generation ( ) ;
} ) ;
}
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , value ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetGlobal ) ) ;
}
auto completion = vm - > throw_completion < ReferenceError > ( ErrorType : : UnknownIdentifier , identifier ) ;
return handle_asm_exception ( * vm , pc , completion . value ( ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_try_set_global_env_binding ( VM * vm , u32 , Op : : SetGlobal const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & cache = vm - > current_executable ( ) . global_variable_caches [ instruction - > cache ( ) ] ;
2026-06-14 11:07:00 -03:00
if ( ! cache . has_environment_binding_index ) [[unlikely]]
return 1 ;
auto & current_vm = * vm ;
2026-06-14 09:30:56 -03:00
auto src = vm - > get ( instruction - > src ( ) ) ;
2026-06-14 11:07:00 -03:00
ThrowCompletionOr < void > result ;
if ( cache . in_module_environment ) {
auto module = current_vm . running_execution_context ( ) . script_or_module . get_pointer < GC : : Ref < Module > > ( ) ;
if ( ! module ) [[unlikely]]
return 1 ;
2026-06-14 09:30:56 -03:00
result = ( * module ) - > environment ( ) - > set_mutable_binding_direct ( current_vm , cache . environment_binding_index , src , instruction - > strict ( ) = = Strict : : Yes ) ;
2026-06-14 11:07:00 -03:00
} else {
2026-06-14 09:30:56 -03:00
result = vm - > global_declarative_environment ( ) . set_mutable_binding_direct ( current_vm , cache . environment_binding_index , src , instruction - > strict ( ) = = Strict : : Yes ) ;
2026-06-14 11:07:00 -03:00
}
if ( result . is_error ( ) ) [[unlikely]]
return 1 ;
return 0 ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_set_global ( VM * vm , u32 pc , Op : : SetGlobal const * instruction )
2026-06-14 11:07:00 -03:00
{
auto & binding_object = vm - > global_object ( ) ;
auto & declarative_record = vm - > global_declarative_environment ( ) ;
2026-06-14 09:30:56 -03:00
auto & cache = vm - > current_executable ( ) . global_variable_caches [ instruction - > cache ( ) ] ;
2026-06-14 11:07:00 -03:00
auto & shape = binding_object . shape ( ) ;
2026-06-14 09:30:56 -03:00
auto src = vm - > get ( instruction - > src ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( cache . environment_serial_number = = declarative_record . environment_serial_number ( ) ) {
auto * entry = cache . first_entry ( ) ;
if ( entry & & & shape = = entry - > shape & & ( ! shape . is_dictionary ( ) | | shape . dictionary_generation ( ) = = entry - > shape_dictionary_generation ) ) {
auto value = binding_object . get_direct ( entry - > property_offset ) ;
if ( value . is_accessor ( ) )
ASM_TRY ( * vm , pc , call ( * vm , value . as_accessor ( ) . setter ( ) , & binding_object , src ) ) ;
else
binding_object . put_direct ( entry - > property_offset , src ) ;
return static_cast < i64 > ( pc + sizeof ( Op : : SetGlobal ) ) ;
}
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 > > ( ) ;
2026-06-14 09:30:56 -03:00
ASM_TRY ( * vm , pc , ( * module ) - > environment ( ) - > set_mutable_binding_direct ( * vm , cache . environment_binding_index , src , instruction - > strict ( ) = = Strict : : Yes ) ) ;
2026-06-14 11:07:00 -03:00
} else {
2026-06-14 09:30:56 -03:00
ASM_TRY ( * vm , pc , declarative_record . set_mutable_binding_direct ( * vm , cache . environment_binding_index , src , instruction - > strict ( ) = = Strict : : Yes ) ) ;
2026-06-14 11:07:00 -03:00
}
return static_cast < i64 > ( pc + sizeof ( Op : : SetGlobal ) ) ;
}
}
cache . environment_serial_number = declarative_record . environment_serial_number ( ) ;
2026-06-14 09:30:56 -03:00
auto & identifier = vm - > get_identifier ( instruction - > identifier ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( auto * module = vm - > running_execution_context ( ) . script_or_module . get_pointer < GC : : Ref < Module > > ( ) ) {
auto & module_environment = * ( * module ) - > environment ( ) ;
Optional < size_t > index ;
if ( ASM_TRY ( * vm , pc , 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 ;
2026-06-14 09:30:56 -03:00
ASM_TRY ( * vm , pc , module_environment . set_mutable_binding_direct ( * vm , index . value ( ) , src , instruction - > strict ( ) = = Strict : : Yes ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : SetGlobal ) ) ;
}
2026-06-14 09:30:56 -03:00
ASM_TRY ( * vm , pc , module_environment . set_mutable_binding ( * vm , identifier , src , instruction - > strict ( ) = = Strict : : Yes ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : SetGlobal ) ) ;
}
}
Optional < size_t > offset ;
if ( ASM_TRY ( * vm , pc , 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 ;
2026-06-14 09:30:56 -03:00
ASM_TRY ( * vm , pc , declarative_record . set_mutable_binding ( * vm , identifier , src , instruction - > strict ( ) = = Strict : : Yes ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : SetGlobal ) ) ;
}
if ( ASM_TRY ( * vm , pc , binding_object . has_property ( identifier ) ) ) {
CacheableSetPropertyMetadata cacheable_metadata ;
auto success = ASM_TRY ( * vm , pc , binding_object . internal_set ( identifier , src , & binding_object , & cacheable_metadata ) ) ;
2026-06-14 09:30:56 -03:00
if ( ! success & & instruction - > strict ( ) = = Strict : : Yes ) [[unlikely]] {
2026-06-14 11:07:00 -03:00
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 ) ) {
auto completion = vm - > throw_completion < TypeError > ( ErrorType : : DescWriteNonWritable , identifier ) ;
return handle_asm_exception ( * vm , pc , completion . value ( ) ) ;
}
}
auto completion = vm - > throw_completion < TypeError > ( ErrorType : : ObjectSetReturnedFalse ) ;
return handle_asm_exception ( * vm , pc , completion . value ( ) ) ;
}
if ( cacheable_metadata . type = = CacheableSetPropertyMetadata : : Type : : ChangeOwnProperty ) {
cache . update ( PropertyLookupCache : : Entry : : Type : : ChangeOwnProperty , [ & ] ( auto & entry ) {
entry . shape = shape ;
entry . property_offset = cacheable_metadata . property_offset . value ( ) ;
if ( shape . is_dictionary ( ) )
entry . shape_dictionary_generation = shape . dictionary_generation ( ) ;
} ) ;
}
return static_cast < i64 > ( pc + sizeof ( Op : : SetGlobal ) ) ;
}
2026-06-14 09:30:56 -03:00
auto reference = ASM_TRY ( * vm , pc , vm - > resolve_binding ( identifier , instruction - > strict ( ) , & declarative_record ) ) ;
2026-06-14 11:07:00 -03:00
ASM_TRY ( * vm , pc , reference . put_value ( * vm , src ) ) ;
return static_cast < i64 > ( pc + sizeof ( Op : : SetGlobal ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_concat_string ( VM * vm , u32 pc , Op : : ConcatString const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto string = ASM_TRY ( * vm , pc , vm - > get ( instruction - > src ( ) ) . to_primitive_string ( * vm ) ) ;
vm - > set ( instruction - > dst ( ) , PrimitiveString : : create ( * vm , vm - > get ( instruction - > dst ( ) ) . as_string ( ) , string ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : ConcatString ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_copy_object_excluding_properties ( VM * vm , u32 pc , Op : : CopyObjectExcludingProperties const * instruction )
2026-06-14 11:07:00 -03:00
{
auto & realm = * vm - > current_realm ( ) ;
2026-06-14 09:30:56 -03:00
auto from_object = vm - > get ( instruction - > from_object ( ) ) ;
2026-06-14 11:07:00 -03:00
auto to_object = Object : : create ( realm , realm . intrinsics ( ) . object_prototype ( ) ) ;
GC : : ConservativeHashTable < PropertyKey > excluded_names ;
2026-06-14 09:30:56 -03:00
auto excluded_names_operands = instruction - > excluded_names ( ) ;
for ( size_t i = 0 ; i < instruction - > excluded_names_count ( ) ; + + i )
2026-06-14 11:07:00 -03:00
excluded_names . set ( ASM_TRY ( * vm , pc , vm - > get ( excluded_names_operands [ i ] ) . to_property_key ( * vm ) ) ) ;
ASM_TRY ( * vm , pc , to_object - > copy_data_properties ( * vm , from_object , excluded_names ) ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , to_object ) ;
return static_cast < i64 > ( pc + instruction - > length ( ) ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_exp ( VM * vm , u32 pc , Op : : Exp const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto result = ASM_TRY ( * vm , pc , exp ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) ;
vm - > set ( instruction - > dst ( ) , result ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : Exp ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_import_call ( VM * vm , u32 pc , Op : : ImportCall const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto specifier = vm - > get ( instruction - > specifier ( ) ) ;
auto options_value = vm - > get ( instruction - > options ( ) ) ;
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , perform_import_call ( * vm , specifier , options_value ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : ImportCall ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_new_class ( VM * vm , u32 pc , Op : : NewClass const * instruction )
2026-06-14 11:07:00 -03:00
{
Value super_class ;
2026-06-14 09:30:56 -03:00
if ( instruction - > super_class ( ) . has_value ( ) )
super_class = vm - > get ( instruction - > super_class ( ) . value ( ) ) ;
2026-06-14 11:07:00 -03:00
GC : : RootVector < Value > element_keys ;
2026-06-14 09:30:56 -03:00
element_keys . ensure_capacity ( instruction - > element_keys_count ( ) ) ;
for ( size_t i = 0 ; i < instruction - > element_keys_count ( ) ; + + i ) {
2026-06-14 11:07:00 -03:00
Value element_key ;
2026-06-14 09:30:56 -03:00
if ( instruction - > element_keys ( ) [ i ] . has_value ( ) )
element_key = vm - > get ( instruction - > element_keys ( ) [ i ] . value ( ) ) ;
2026-06-14 11:07:00 -03:00
element_keys . unchecked_append ( element_key ) ;
}
auto & running_execution_context = vm - > running_execution_context ( ) ;
2026-06-14 09:30:56 -03:00
auto * class_environment = & as < Environment > ( vm - > get ( instruction - > class_environment ( ) ) . as_cell ( ) ) ;
2026-06-14 11:07:00 -03:00
auto & outer_environment = running_execution_context . lexical_environment ;
2026-06-14 09:30:56 -03:00
auto const & blueprint = vm - > current_executable ( ) . class_blueprints [ instruction - > class_blueprint_index ( ) ] ;
2026-06-14 11:07:00 -03:00
Optional < Utf16FlyString > binding_name ;
Utf16FlyString class_name ;
2026-06-14 09:30:56 -03:00
if ( ! blueprint . has_name & & instruction - > lhs_name ( ) . has_value ( ) ) {
class_name = vm - > get_identifier ( instruction - > lhs_name ( ) . value ( ) ) ;
2026-06-14 11:07:00 -03:00
} else {
class_name = blueprint . name ;
binding_name = class_name ;
}
auto * retval = ASM_TRY ( * vm , pc , construct_class ( * vm , blueprint , vm - > current_executable ( ) , class_environment , outer_environment , super_class , element_keys , binding_name , class_name ) ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , retval ) ;
return static_cast < i64 > ( pc + instruction - > length ( ) ) ;
2026-06-14 11:07:00 -03:00
}
static COLD Completion throw_type_error_for_asm_callee ( VM & vm , Value callee , StringView callee_type , Optional < StringTableIndex > const expression_string )
{
if ( expression_string . has_value ( ) )
return vm . throw_completion < TypeError > ( ErrorType : : IsNotAEvaluatedFrom , callee , callee_type , vm . current_executable ( ) . get_string ( * expression_string ) ) ;
return vm . throw_completion < TypeError > ( ErrorType : : IsNotA , callee , callee_type ) ;
}
static ThrowCompletionOr < void > throw_if_needed_for_asm_call ( VM & vm , 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 ( ) ) [[unlikely]]
return throw_type_error_for_asm_callee ( vm , callee , " function " sv , expression_string ) ;
if ( call_type = = Op : : CallType : : Construct & & ! callee . is_constructor ( ) ) [[unlikely]]
return throw_type_error_for_asm_callee ( vm , callee , " constructor " sv , expression_string ) ;
return { } ;
}
NEVER_INLINE static ThrowCompletionOr < void > execute_asm_call (
Op : : CallType call_type ,
VM & vm ,
Value callee ,
Value this_value ,
ReadonlySpan < Operand > arguments ,
Operand dst ,
Optional < StringTableIndex > const expression_string ,
Strict strict )
{
TRY ( throw_if_needed_for_asm_call ( vm , callee , call_type , expression_string ) ) ;
auto & function = callee . as_function ( ) ;
size_t registers_and_locals_count = 0 ;
ReadonlySpan < Value > constants ;
size_t argument_count = arguments . size ( ) ;
function . get_stack_frame_info ( registers_and_locals_count , constants , argument_count ) ;
auto & stack = vm . interpreter_stack ( ) ;
auto * stack_mark = stack . top ( ) ;
auto * callee_context = stack . allocate ( registers_and_locals_count , constants , max ( arguments . size ( ) , argument_count ) ) ;
if ( ! callee_context ) [[unlikely]]
return vm . throw_completion < InternalError > ( ErrorType : : CallStackSizeExceeded ) ;
2026-06-14 11:07:07 -03:00
ScopeGuard deallocate_guard = [ & stack , stack_mark ] {
if ( stack . top ( ) > stack_mark )
stack . deallocate ( stack_mark ) ;
} ;
2026-06-14 11:07:00 -03:00
auto * callee_context_argument_values = callee_context - > arguments_data ( ) ;
auto const callee_context_argument_count = callee_context - > argument_count ;
auto const insn_argument_count = arguments . size ( ) ;
for ( size_t i = 0 ; i < insn_argument_count ; + + i )
callee_context_argument_values [ i ] = vm . get ( arguments . data ( ) [ i ] ) ;
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 = = Op : : CallType : : DirectEval ) {
if ( callee = = vm . realm ( ) . intrinsics ( ) . eval_function ( ) ) {
retval = TRY ( perform_eval ( vm , callee_context - > argument_count > 0 ? callee_context - > arguments_data ( ) [ 0 ] : js_undefined ( ) , strict = = Strict : : Yes ? CallerMode : : Strict : CallerMode : : NonStrict , EvalMode : : Direct ) ) ;
} else {
retval = TRY ( function . internal_call ( * callee_context , this_value ) ) ;
}
} else if ( call_type = = Op : : CallType : : Construct ) {
retval = TRY ( function . internal_construct ( * callee_context , function ) ) ;
} else {
retval = TRY ( function . internal_call ( * callee_context , this_value ) ) ;
}
vm . set ( dst , retval ) ;
return { } ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_call ( VM * vm , u32 pc , Op : : Call const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
ASM_TRY ( * vm , pc , execute_asm_call ( Op : : CallType : : Call , * vm , vm - > get ( instruction - > callee ( ) ) , vm - > get ( instruction - > this_value ( ) ) , instruction - > arguments ( ) , instruction - > dst ( ) , instruction - > expression_string ( ) , instruction - > strict ( ) ) ) ;
return static_cast < i64 > ( pc + instruction - > length ( ) ) ;
2026-06-14 11:07:00 -03:00
}
static ThrowCompletionOr < void > call_direct_eval (
VM & vm ,
Value callee ,
Value this_value ,
ReadonlySpan < Operand > arguments ,
Operand dst ,
Optional < StringTableIndex > const expression_string ,
Strict strict )
{
TRY ( throw_if_needed_for_asm_call ( vm , callee , Op : : CallType : : DirectEval , expression_string ) ) ;
auto & function = callee . as_function ( ) ;
size_t registers_and_locals_count = 0 ;
ReadonlySpan < Value > constants ;
size_t argument_count = arguments . size ( ) ;
function . get_stack_frame_info ( registers_and_locals_count , constants , argument_count ) ;
auto & stack = vm . interpreter_stack ( ) ;
auto * stack_mark = stack . top ( ) ;
auto * callee_context = stack . allocate ( registers_and_locals_count , constants , max ( arguments . size ( ) , argument_count ) ) ;
if ( ! callee_context ) [[unlikely]]
return vm . throw_completion < InternalError > ( ErrorType : : CallStackSizeExceeded ) ;
ScopeGuard deallocate_guard = [ & stack , stack_mark ] { stack . deallocate ( stack_mark ) ; } ;
auto * callee_context_argument_values = callee_context - > arguments_data ( ) ;
auto const callee_context_argument_count = callee_context - > argument_count ;
auto const insn_argument_count = arguments . size ( ) ;
for ( size_t i = 0 ; i < insn_argument_count ; + + i )
callee_context_argument_values [ i ] = vm . get ( arguments . data ( ) [ i ] ) ;
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 ( callee = = vm . realm ( ) . intrinsics ( ) . eval_function ( ) ) {
retval = TRY ( perform_eval ( vm , callee_context - > argument_count > 0 ? callee_context - > arguments_data ( ) [ 0 ] : js_undefined ( ) , strict = = Strict : : Yes ? CallerMode : : Strict : CallerMode : : NonStrict , EvalMode : : Direct ) ) ;
} else {
retval = TRY ( function . internal_call ( * callee_context , this_value ) ) ;
}
vm . set ( dst , retval ) ;
return { } ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_call_direct_eval ( VM * vm , u32 pc , Op : : CallDirectEval const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
ASM_TRY ( * vm , pc , call_direct_eval ( * vm , vm - > get ( instruction - > callee ( ) ) , vm - > get ( instruction - > this_value ( ) ) , instruction - > arguments ( ) , instruction - > dst ( ) , instruction - > expression_string ( ) , instruction - > strict ( ) ) ) ;
return static_cast < i64 > ( pc + instruction - > length ( ) ) ;
2026-06-14 11:07:00 -03:00
}
static ThrowCompletionOr < void > call_with_argument_array (
Op : : CallType call_type ,
VM & vm ,
Value callee ,
Value this_value ,
Value arguments ,
Operand dst ,
Optional < StringTableIndex > const expression_string ,
Strict strict )
{
TRY ( throw_if_needed_for_asm_call ( vm , callee , call_type , expression_string ) ) ;
auto & function = callee . as_function ( ) ;
auto & argument_array = arguments . as_array_exotic_object ( ) ;
auto argument_array_length = argument_array . indexed_array_like_size ( ) ;
size_t argument_count = argument_array_length ;
size_t registers_and_locals_count = 0 ;
ReadonlySpan < Value > constants ;
function . get_stack_frame_info ( registers_and_locals_count , constants , argument_count ) ;
auto & stack = vm . interpreter_stack ( ) ;
auto * stack_mark = stack . top ( ) ;
auto * callee_context = stack . allocate ( registers_and_locals_count , constants , max ( argument_array_length , argument_count ) ) ;
if ( ! callee_context ) [[unlikely]]
return vm . throw_completion < InternalError > ( ErrorType : : CallStackSizeExceeded ) ;
2026-06-14 11:07:07 -03:00
ScopeGuard deallocate_guard = [ & stack , stack_mark ] {
if ( stack . top ( ) > stack_mark )
stack . deallocate ( stack_mark ) ;
} ;
2026-06-14 11:07:00 -03:00
auto * callee_context_argument_values = callee_context - > arguments_data ( ) ;
auto const callee_context_argument_count = callee_context - > argument_count ;
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_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 = = Op : : CallType : : DirectEval & & callee = = vm . realm ( ) . intrinsics ( ) . eval_function ( ) ) {
retval = TRY ( perform_eval ( vm , callee_context - > argument_count > 0 ? callee_context - > arguments_data ( ) [ 0 ] : js_undefined ( ) , strict = = Strict : : Yes ? CallerMode : : Strict : CallerMode : : NonStrict , EvalMode : : Direct ) ) ;
} else if ( call_type = = Op : : CallType : : Construct ) {
retval = TRY ( function . internal_construct ( * callee_context , function ) ) ;
} else {
retval = TRY ( function . internal_call ( * callee_context , this_value ) ) ;
}
vm . set ( dst , retval ) ;
return { } ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_call_with_argument_array ( VM * vm , u32 pc , Op : : CallWithArgumentArray const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
ASM_TRY ( * vm , pc , call_with_argument_array ( Op : : CallType : : Call , * vm , vm - > get ( instruction - > callee ( ) ) , vm - > get ( instruction - > this_value ( ) ) , vm - > get ( instruction - > arguments ( ) ) , instruction - > dst ( ) , instruction - > expression_string ( ) , instruction - > strict ( ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : CallWithArgumentArray ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_call_direct_eval_with_argument_array ( VM * vm , u32 pc , Op : : CallDirectEvalWithArgumentArray const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
ASM_TRY ( * vm , pc , call_with_argument_array ( Op : : CallType : : DirectEval , * vm , vm - > get ( instruction - > callee ( ) ) , vm - > get ( instruction - > this_value ( ) ) , vm - > get ( instruction - > arguments ( ) ) , instruction - > dst ( ) , instruction - > expression_string ( ) , instruction - > strict ( ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : CallDirectEvalWithArgumentArray ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_object_property_iterator ( VM * vm , u32 pc , Op : : GetObjectPropertyIterator const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto * cache = & vm - > current_executable ( ) . object_property_iterator_caches [ instruction - > cache ( ) ] ;
vm - > set ( instruction - > dst_iterator ( ) , ASM_TRY ( * vm , pc , asm_get_object_property_iterator ( * vm , vm - > get ( instruction - > object ( ) ) , cache ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetObjectPropertyIterator ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_object_property_iterator_next ( VM * vm , u32 pc , Op : : ObjectPropertyIteratorNext const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & iterator = static_cast < PropertyNameIterator & > ( vm - > get ( instruction - > iterator_object ( ) ) . as_object ( ) ) ;
2026-06-14 11:07:00 -03:00
Value value ;
bool done = false ;
ASM_TRY ( * vm , pc , iterator . next ( * vm , done , value ) ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst_done ( ) , Value ( done ) ) ;
2026-06-14 11:07:00 -03:00
if ( ! done )
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst_value ( ) , value ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : ObjectPropertyIteratorNext ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_iterator_close ( VM * vm , u32 pc , Op : : IteratorClose const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & iterator_object = vm - > get ( instruction - > iterator_object ( ) ) . as_object ( ) ;
auto iterator_next_method = vm - > get ( instruction - > iterator_next ( ) ) ;
auto iterator_done_property = vm - > get ( instruction - > iterator_done ( ) ) . as_bool ( ) ;
2026-06-14 11:07:00 -03:00
IteratorRecordImpl iterator_record { . done = iterator_done_property , . iterator = iterator_object , . next_method = iterator_next_method } ;
2026-06-14 09:30:56 -03:00
ASM_TRY ( * vm , pc , iterator_close ( * vm , iterator_record , Completion { instruction - > completion_type ( ) , vm - > get ( instruction - > completion_value ( ) ) } ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : IteratorClose ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_iterator_next ( VM * vm , u32 pc , Op : : IteratorNext const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & iterator_object = vm - > get ( instruction - > iterator_object ( ) ) . as_object ( ) ;
auto iterator_next_method = vm - > get ( instruction - > iterator_next ( ) ) ;
auto iterator_done_property = vm - > get ( instruction - > iterator_done ( ) ) . as_bool ( ) ;
2026-06-14 11:07:00 -03:00
IteratorRecordImpl iterator_record { . done = iterator_done_property , . iterator = iterator_object , . next_method = iterator_next_method } ;
auto result = iterator_next ( * vm , iterator_record ) ;
if ( iterator_record . done )
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > iterator_done ( ) , Value ( true ) ) ;
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , result ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : IteratorNext ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_iterator_next_unpack ( VM * vm , u32 pc , Op : : IteratorNextUnpack const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & iterator_object = vm - > get ( instruction - > iterator_object ( ) ) . as_object ( ) ;
auto iterator_next_method = vm - > get ( instruction - > iterator_next ( ) ) ;
auto iterator_done_property = vm - > get ( instruction - > iterator_done ( ) ) . as_bool ( ) ;
2026-06-14 11:07:00 -03:00
IteratorRecordImpl iterator_record { . done = iterator_done_property , . iterator = iterator_object , . next_method = iterator_next_method } ;
auto iteration_result_or_done_or_error = iterator_step ( * vm , iterator_record ) ;
if ( iterator_record . done )
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > iterator_done ( ) , Value ( true ) ) ;
2026-06-14 11:07:00 -03:00
auto iteration_result_or_done = ASM_TRY ( * vm , pc , iteration_result_or_done_or_error ) ;
if ( iteration_result_or_done . has < IterationDone > ( ) ) {
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst_done ( ) , Value ( true ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : IteratorNextUnpack ) ) ;
}
auto & iteration_result = iteration_result_or_done . get < IterationResult > ( ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst_done ( ) , ASM_TRY ( * vm , pc , iteration_result . done ) ) ;
2026-06-14 11:07:00 -03:00
auto value = move ( iteration_result . value ) ;
if ( value . is_throw_completion ( ) )
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > iterator_done ( ) , Value ( true ) ) ;
vm - > set ( instruction - > dst_value ( ) , ASM_TRY ( * vm , pc , value ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : IteratorNextUnpack ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_iterator_to_array ( VM * vm , u32 pc , Op : : IteratorToArray const * instruction )
2026-06-14 11:07:00 -03:00
{
IteratorRecordImpl iterator_record {
2026-06-14 09:30:56 -03:00
. done = vm - > get ( instruction - > iterator_done_property ( ) ) . as_bool ( ) ,
. iterator = vm - > get ( instruction - > iterator_object ( ) ) . as_object ( ) ,
. next_method = vm - > get ( instruction - > iterator_next_method ( ) )
2026-06-14 11:07:00 -03:00
} ;
auto array = MUST ( JS : : Array : : create ( * vm - > current_realm ( ) , 0 ) ) ;
size_t index = 0 ;
while ( true ) {
auto value_or_error = iterator_step_value ( * vm , iterator_record ) ;
if ( iterator_record . done )
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > iterator_done_property ( ) , Value ( true ) ) ;
2026-06-14 11:07:00 -03:00
auto value = ASM_TRY ( * vm , pc , value_or_error ) ;
if ( ! value . has_value ( ) ) {
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , array ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : IteratorToArray ) ) ;
}
MUST ( array - > create_data_property_or_throw ( index , value . release_value ( ) ) ) ;
+ + index ;
}
}
2026-06-14 09:30:56 -03:00
# define JS_DEFINE_UNARY_BUILTIN_CALL_SLOW_PATH(name, snake_case_name, implementation) \
i64 asm_slow_path_call_builtin_ # # snake_case_name ( VM * vm , u32 pc , Op : : CallBuiltin # # name const * instruction ) \
{ \
Operand arguments [ ] { instruction - > argument ( ) } ; \
auto callee = vm - > get ( instruction - > callee ( ) ) ; \
if ( callee . is_function ( ) & & callee . as_function ( ) . builtin ( ) = = Builtin : : name ) { \
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , implementation ( * vm , vm - > get ( instruction - > argument ( ) ) ) ) ) ; \
return static_cast < i64 > ( pc + sizeof ( Op : : CallBuiltin # # name ) ) ; \
} \
ASM_TRY ( * vm , pc , execute_asm_call ( Op : : CallType : : Call , * vm , callee , vm - > get ( instruction - > this_value ( ) ) , arguments , instruction - > dst ( ) , instruction - > expression_string ( ) , instruction - > strict ( ) ) ) ; \
return static_cast < i64 > ( pc + sizeof ( Op : : CallBuiltin # # name ) ) ; \
}
# define JS_DEFINE_BINARY_BUILTIN_CALL_SLOW_PATH(name, snake_case_name, implementation) \
i64 asm_slow_path_call_builtin_ # # snake_case_name ( VM * vm , u32 pc , Op : : CallBuiltin # # name const * instruction ) \
{ \
Operand arguments [ ] { instruction - > argument0 ( ) , instruction - > argument1 ( ) } ; \
auto callee = vm - > get ( instruction - > callee ( ) ) ; \
if ( callee . is_function ( ) & & callee . as_function ( ) . builtin ( ) = = Builtin : : name ) { \
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , implementation ( * vm , vm - > get ( instruction - > argument0 ( ) ) , vm - > get ( instruction - > argument1 ( ) ) ) ) ) ; \
return static_cast < i64 > ( pc + sizeof ( Op : : CallBuiltin # # name ) ) ; \
} \
ASM_TRY ( * vm , pc , execute_asm_call ( Op : : CallType : : Call , * vm , callee , vm - > get ( instruction - > this_value ( ) ) , arguments , instruction - > dst ( ) , instruction - > expression_string ( ) , instruction - > strict ( ) ) ) ; \
return static_cast < i64 > ( pc + sizeof ( Op : : CallBuiltin # # name ) ) ; \
}
# define JS_DEFINE_NULLARY_BUILTIN_CALL_SLOW_PATH(name, snake_case_name, implementation) \
i64 asm_slow_path_call_builtin_ # # snake_case_name ( VM * vm , u32 pc , Op : : CallBuiltin # # name const * instruction ) \
{ \
auto callee = vm - > get ( instruction - > callee ( ) ) ; \
if ( callee . is_function ( ) & & callee . as_function ( ) . builtin ( ) = = Builtin : : name ) { \
vm - > set ( instruction - > dst ( ) , implementation ( ) ) ; \
return static_cast < i64 > ( pc + sizeof ( Op : : CallBuiltin # # name ) ) ; \
} \
ASM_TRY ( * vm , pc , execute_asm_call ( Op : : CallType : : Call , * vm , callee , vm - > get ( instruction - > this_value ( ) ) , { } , instruction - > dst ( ) , instruction - > expression_string ( ) , instruction - > strict ( ) ) ) ; \
return static_cast < i64 > ( pc + sizeof ( Op : : CallBuiltin # # name ) ) ; \
}
# define JS_DEFINE_GENERIC_BUILTIN_CALL_SLOW_PATH(name, snake_case_name, ...) \
i64 asm_slow_path_call_builtin_ # # snake_case_name ( VM * vm , u32 pc , Op : : CallBuiltin # # name const * instruction ) \
{ \
ASM_TRY ( * vm , pc , execute_asm_call ( Op : : CallType : : Call , * vm , vm - > get ( instruction - > callee ( ) ) , vm - > get ( instruction - > this_value ( ) ) , { } , instruction - > dst ( ) , instruction - > expression_string ( ) , instruction - > strict ( ) ) ) ; \
return static_cast < i64 > ( pc + sizeof ( Op : : CallBuiltin # # name ) ) ; \
}
# define JS_DEFINE_UNARY_GENERIC_BUILTIN_CALL_SLOW_PATH(name, snake_case_name, ...) \
i64 asm_slow_path_call_builtin_ # # snake_case_name ( VM * vm , u32 pc , Op : : CallBuiltin # # name const * instruction ) \
{ \
Operand arguments [ ] { instruction - > argument ( ) } ; \
ASM_TRY ( * vm , pc , execute_asm_call ( Op : : CallType : : Call , * vm , vm - > get ( instruction - > callee ( ) ) , vm - > get ( instruction - > this_value ( ) ) , arguments , instruction - > dst ( ) , instruction - > expression_string ( ) , instruction - > strict ( ) ) ) ; \
return static_cast < i64 > ( pc + sizeof ( Op : : CallBuiltin # # name ) ) ; \
}
# define JS_DEFINE_BINARY_GENERIC_BUILTIN_CALL_SLOW_PATH(name, snake_case_name, ...) \
i64 asm_slow_path_call_builtin_ # # snake_case_name ( VM * vm , u32 pc , Op : : CallBuiltin # # name const * instruction ) \
{ \
Operand arguments [ ] { instruction - > argument0 ( ) , instruction - > argument1 ( ) } ; \
ASM_TRY ( * vm , pc , execute_asm_call ( Op : : CallType : : Call , * vm , vm - > get ( instruction - > callee ( ) ) , vm - > get ( instruction - > this_value ( ) ) , arguments , instruction - > dst ( ) , instruction - > expression_string ( ) , instruction - > strict ( ) ) ) ; \
return static_cast < i64 > ( pc + sizeof ( Op : : CallBuiltin # # name ) ) ; \
2026-06-14 11:07:00 -03:00
}
JS_DEFINE_UNARY_BUILTIN_CALL_SLOW_PATH ( MathAbs , math_abs , MathObject : : abs_impl )
JS_DEFINE_UNARY_BUILTIN_CALL_SLOW_PATH ( MathLog , math_log , MathObject : : log_impl )
JS_DEFINE_BINARY_BUILTIN_CALL_SLOW_PATH ( MathPow , math_pow , MathObject : : pow_impl )
JS_DEFINE_UNARY_BUILTIN_CALL_SLOW_PATH ( MathExp , math_exp , MathObject : : exp_impl )
JS_DEFINE_UNARY_BUILTIN_CALL_SLOW_PATH ( MathCeil , math_ceil , MathObject : : ceil_impl )
JS_DEFINE_UNARY_BUILTIN_CALL_SLOW_PATH ( MathFloor , math_floor , MathObject : : floor_impl )
JS_DEFINE_BINARY_BUILTIN_CALL_SLOW_PATH ( MathImul , math_imul , MathObject : : imul_impl )
JS_DEFINE_NULLARY_BUILTIN_CALL_SLOW_PATH ( MathRandom , math_random , MathObject : : random_impl )
JS_DEFINE_UNARY_BUILTIN_CALL_SLOW_PATH ( MathRound , math_round , MathObject : : round_impl )
JS_DEFINE_UNARY_BUILTIN_CALL_SLOW_PATH ( MathSqrt , math_sqrt , MathObject : : sqrt_impl )
JS_DEFINE_UNARY_BUILTIN_CALL_SLOW_PATH ( MathSin , math_sin , MathObject : : sin_impl )
JS_DEFINE_UNARY_BUILTIN_CALL_SLOW_PATH ( MathCos , math_cos , MathObject : : cos_impl )
JS_DEFINE_UNARY_BUILTIN_CALL_SLOW_PATH ( MathTan , math_tan , MathObject : : tan_impl )
JS_DEFINE_UNARY_GENERIC_BUILTIN_CALL_SLOW_PATH ( RegExpPrototypeExec , regexp_prototype_exec )
JS_DEFINE_BINARY_GENERIC_BUILTIN_CALL_SLOW_PATH ( RegExpPrototypeReplace , regexp_prototype_replace )
JS_DEFINE_BINARY_GENERIC_BUILTIN_CALL_SLOW_PATH ( RegExpPrototypeSplit , regexp_prototype_split )
JS_DEFINE_UNARY_GENERIC_BUILTIN_CALL_SLOW_PATH ( OrdinaryHasInstance , ordinary_has_instance )
JS_DEFINE_GENERIC_BUILTIN_CALL_SLOW_PATH ( ArrayIteratorPrototypeNext , array_iterator_prototype_next )
JS_DEFINE_GENERIC_BUILTIN_CALL_SLOW_PATH ( MapIteratorPrototypeNext , map_iterator_prototype_next )
JS_DEFINE_GENERIC_BUILTIN_CALL_SLOW_PATH ( SetIteratorPrototypeNext , set_iterator_prototype_next )
JS_DEFINE_GENERIC_BUILTIN_CALL_SLOW_PATH ( StringIteratorPrototypeNext , string_iterator_prototype_next )
JS_DEFINE_UNARY_BUILTIN_CALL_SLOW_PATH ( StringFromCharCode , string_from_char_code , StringConstructor : : from_char_code_impl )
JS_DEFINE_UNARY_GENERIC_BUILTIN_CALL_SLOW_PATH ( StringPrototypeCharCodeAt , string_prototype_char_code_at )
JS_DEFINE_UNARY_GENERIC_BUILTIN_CALL_SLOW_PATH ( StringPrototypeCharAt , string_prototype_char_at )
# undef JS_DEFINE_BINARY_GENERIC_BUILTIN_CALL_SLOW_PATH
# undef JS_DEFINE_UNARY_GENERIC_BUILTIN_CALL_SLOW_PATH
# undef JS_DEFINE_GENERIC_BUILTIN_CALL_SLOW_PATH
# undef JS_DEFINE_NULLARY_BUILTIN_CALL_SLOW_PATH
# undef JS_DEFINE_BINARY_BUILTIN_CALL_SLOW_PATH
# undef JS_DEFINE_UNARY_BUILTIN_CALL_SLOW_PATH
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_call_construct ( VM * vm , u32 pc , Op : : CallConstruct const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
ASM_TRY ( * vm , pc , execute_asm_call ( Op : : CallType : : Construct , * vm , vm - > get ( instruction - > callee ( ) ) , js_undefined ( ) , instruction - > arguments ( ) , instruction - > dst ( ) , instruction - > expression_string ( ) , instruction - > strict ( ) ) ) ;
return static_cast < i64 > ( pc + instruction - > length ( ) ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_call_construct_with_argument_array ( VM * vm , u32 pc , Op : : CallConstructWithArgumentArray const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
ASM_TRY ( * vm , pc , call_with_argument_array ( Op : : CallType : : Construct , * vm , vm - > get ( instruction - > callee ( ) ) , js_undefined ( ) , vm - > get ( instruction - > arguments ( ) ) , instruction - > dst ( ) , instruction - > expression_string ( ) , instruction - > strict ( ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : CallConstructWithArgumentArray ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_super_call_with_argument_array ( VM * vm , u32 pc , Op : : SuperCallWithArgumentArray const * instruction )
2026-06-14 11:07:00 -03:00
{
auto new_target = vm - > get_new_target ( ) ;
VERIFY ( new_target . is_object ( ) ) ;
2026-06-14 09:30:56 -03:00
auto super_constructor = vm - > get ( instruction - > super_constructor ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( ! super_constructor . is_constructor ( ) ) [[unlikely]] {
vm - > running_execution_context ( ) . program_counter = pc ;
auto completion = vm - > throw_completion < TypeError > ( ErrorType : : NotAConstructor , " Super constructor " ) ;
return handle_asm_exception ( * vm , pc , completion . value ( ) ) ;
}
auto & function = super_constructor . as_function ( ) ;
2026-06-14 09:30:56 -03:00
auto & argument_array = vm - > get ( instruction - > arguments ( ) ) . as_array_exotic_object ( ) ;
2026-06-14 11:07:00 -03:00
size_t argument_array_length = 0 ;
2026-06-14 09:30:56 -03:00
if ( instruction - > is_synthetic ( ) ) {
2026-06-14 11:07:00 -03:00
argument_array_length = MUST ( length_of_array_like ( * vm , argument_array ) ) ;
} else {
argument_array_length = argument_array . indexed_array_like_size ( ) ;
}
size_t argument_count = argument_array_length ;
size_t registers_and_locals_count = 0 ;
ReadonlySpan < Value > constants ;
function . get_stack_frame_info ( registers_and_locals_count , constants , argument_count ) ;
auto & stack = vm - > interpreter_stack ( ) ;
auto * stack_mark = stack . top ( ) ;
auto * callee_context = stack . allocate ( registers_and_locals_count , constants , max ( argument_array_length , argument_count ) ) ;
if ( ! callee_context ) [[unlikely]] {
vm - > running_execution_context ( ) . program_counter = pc ;
auto completion = vm - > throw_completion < InternalError > ( ErrorType : : CallStackSizeExceeded ) ;
return handle_asm_exception ( * vm , pc , completion . value ( ) ) ;
}
2026-06-14 11:07:07 -03:00
ScopeGuard deallocate_guard = [ & stack , stack_mark ] {
if ( stack . top ( ) > stack_mark )
stack . deallocate ( stack_mark ) ;
} ;
2026-06-14 11:07:00 -03:00
auto * callee_context_argument_values = callee_context - > arguments_data ( ) ;
auto const callee_context_argument_count = callee_context - > argument_count ;
auto const insn_argument_count = argument_array_length ;
2026-06-14 09:30:56 -03:00
if ( instruction - > is_synthetic ( ) ) {
2026-06-14 11:07:00 -03:00
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_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 ;
auto result = ASM_TRY ( * vm , pc , function . internal_construct ( * callee_context , new_target . as_function ( ) ) ) ;
auto & this_environment = as < FunctionEnvironment > ( * get_this_environment ( * vm ) ) ;
ASM_TRY ( * vm , pc , this_environment . bind_this_value ( * vm , result ) ) ;
auto & f = as < ECMAScriptFunctionObject > ( this_environment . function_object ( ) ) ;
ASM_TRY ( * vm , pc , result - > initialize_instance_elements ( f ) ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , result ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : SuperCallWithArgumentArray ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_new_object ( VM * vm , u32 pc , Op : : NewObject const * instruction )
2026-06-14 11:07:00 -03:00
{
auto & realm = * vm - > current_realm ( ) ;
2026-06-14 09:30:56 -03:00
if ( instruction - > cache ( ) ! = NumericLimits < u32 > : : max ( ) ) {
auto & cache = vm - > current_executable ( ) . object_shape_caches [ instruction - > cache ( ) ] ;
2026-06-14 11:07:00 -03:00
auto cached_shape = cache . shape . ptr ( ) ;
if ( cached_shape ) {
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , Object : : create_with_premade_shape ( * cached_shape ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : NewObject ) ) ;
}
}
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , Object : : create ( realm , realm . intrinsics ( ) . object_prototype ( ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : NewObject ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_new_object_with_no_prototype ( VM * vm , u32 pc , Op : : NewObjectWithNoPrototype const * instruction )
2026-06-14 11:07:00 -03:00
{
auto & realm = * vm - > current_realm ( ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , Object : : create ( realm , nullptr ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : NewObjectWithNoPrototype ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_cache_object_shape ( VM * vm , u32 pc , Op : : CacheObjectShape const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & cache = vm - > current_executable ( ) . object_shape_caches [ instruction - > cache ( ) ] ;
2026-06-14 11:07:00 -03:00
if ( ! cache . shape ) {
2026-06-14 09:30:56 -03:00
auto & object = vm - > get ( instruction - > object ( ) ) . as_object ( ) ;
2026-06-14 11:07:00 -03:00
if ( ! object . shape ( ) . is_dictionary ( ) )
cache . shape = & object . shape ( ) ;
}
return static_cast < i64 > ( pc + sizeof ( Op : : CacheObjectShape ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_init_object_literal_property ( VM * vm , u32 pc , Op : : InitObjectLiteralProperty const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & object = vm - > get ( instruction - > object ( ) ) . as_object ( ) ;
auto value = vm - > get ( instruction - > src ( ) ) ;
auto & cache = vm - > current_executable ( ) . object_shape_caches [ instruction - > shape_cache_index ( ) ] ;
2026-06-14 11:07:00 -03:00
auto cached_shape = cache . shape . ptr ( ) ;
2026-06-14 09:30:56 -03:00
if ( cached_shape & & & object . shape ( ) = = cached_shape & & instruction - > property_slot ( ) < cache . property_offsets . size ( ) ) {
object . put_direct ( cache . property_offsets [ instruction - > property_slot ( ) ] , value ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : InitObjectLiteralProperty ) ) ;
}
2026-06-14 09:30:56 -03:00
auto const & property_key = vm - > current_executable ( ) . get_property_key ( instruction - > property ( ) ) ;
2026-06-14 11:07:00 -03:00
object . define_direct_property ( property_key , value , JS : : Attribute : : Enumerable | JS : : Attribute : : Writable | JS : : Attribute : : Configurable ) ;
if ( ! object . shape ( ) . is_dictionary ( ) ) {
auto metadata = object . shape ( ) . lookup ( property_key ) ;
if ( metadata . has_value ( ) ) {
2026-06-14 09:30:56 -03:00
if ( instruction - > property_slot ( ) > = cache . property_offsets . size ( ) )
cache . property_offsets . resize ( instruction - > property_slot ( ) + 1 ) ;
cache . property_offsets [ instruction - > property_slot ( ) ] = metadata - > offset ;
2026-06-14 11:07:00 -03:00
}
}
return static_cast < i64 > ( pc + sizeof ( Op : : InitObjectLiteralProperty ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_new_array ( VM * vm , u32 pc , Op : : NewArray const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto array = MUST ( JS : : Array : : create ( vm - > realm ( ) , instruction - > element_count ( ) ) ) ;
for ( size_t i = 0 ; i < instruction - > element_count ( ) ; + + i )
array - > indexed_put ( i , vm - > get ( instruction - > elements ( ) [ i ] ) ) ;
vm - > set ( instruction - > dst ( ) , array ) ;
return static_cast < i64 > ( pc + instruction - > length ( ) ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_new_primitive_array ( VM * vm , u32 pc , Op : : NewPrimitiveArray const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto array = MUST ( JS : : Array : : create ( vm - > realm ( ) , instruction - > element_count ( ) ) ) ;
for ( size_t i = 0 ; i < instruction - > element_count ( ) ; + + i )
array - > indexed_put ( i , instruction - > elements ( ) [ i ] ) ;
vm - > set ( instruction - > dst ( ) , array ) ;
return static_cast < i64 > ( pc + instruction - > length ( ) ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_new_regexp ( VM * vm , u32 pc , Op : : NewRegExp const * instruction )
2026-06-14 11:07:00 -03:00
{
auto & realm = * vm - > current_realm ( ) ;
auto regexp_object = RegExpObject : : create (
realm ,
2026-06-14 09:30:56 -03:00
vm - > current_executable ( ) . get_string ( instruction - > source_index ( ) ) ,
vm - > current_executable ( ) . get_string ( instruction - > flags_index ( ) ) ) ;
2026-06-14 11:07:00 -03:00
regexp_object - > set_realm ( realm ) ;
regexp_object - > set_legacy_features_enabled ( true ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , regexp_object ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : NewRegExp ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_new_reference_error ( VM * vm , u32 pc , Op : : NewReferenceError const * instruction )
2026-06-14 11:07:00 -03:00
{
auto & realm = * vm - > current_realm ( ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ReferenceError : : create ( realm , vm - > current_executable ( ) . get_string ( instruction - > error_string ( ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : NewReferenceError ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_new_type_error ( VM * vm , u32 pc , Op : : NewTypeError const * instruction )
2026-06-14 11:07:00 -03:00
{
auto & realm = * vm - > current_realm ( ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , TypeError : : create ( realm , vm - > current_executable ( ) . get_string ( instruction - > error_string ( ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : NewTypeError ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_bitwise_xor ( VM * vm , u32 pc , Op : : BitwiseXor const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , bitwise_xor ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : BitwiseXor ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_bitwise_and ( VM * vm , u32 pc , Op : : BitwiseAnd const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , bitwise_and ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : BitwiseAnd ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_bitwise_or ( VM * vm , u32 pc , Op : : BitwiseOr const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , bitwise_or ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : BitwiseOr ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_left_shift ( VM * vm , u32 pc , Op : : LeftShift const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , left_shift ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : LeftShift ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_right_shift ( VM * vm , u32 pc , Op : : RightShift const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , right_shift ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : RightShift ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_unsigned_right_shift ( VM * vm , u32 pc , Op : : UnsignedRightShift const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , unsigned_right_shift ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : UnsignedRightShift ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_mod ( VM * vm , u32 pc , Op : : Mod const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , mod ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : Mod ) ) ;
}
static ThrowCompletionOr < bool > loosely_equals ( VM & vm , Value lhs , Value rhs )
{
if ( lhs . tag ( ) = = rhs . tag ( ) ) {
if ( lhs . is_int32 ( ) | | lhs . is_object ( ) | | lhs . is_boolean ( ) | | lhs . is_nullish ( ) )
return lhs . encoded ( ) = = rhs . encoded ( ) ;
}
return TRY ( is_loosely_equal ( vm , lhs , rhs ) ) ;
}
static ThrowCompletionOr < bool > loosely_inequals ( VM & vm , Value lhs , Value rhs )
{
return ! TRY ( loosely_equals ( vm , lhs , rhs ) ) ;
}
static bool strictly_equals ( Value lhs , Value rhs )
{
if ( lhs . tag ( ) = = rhs . tag ( ) ) {
if ( lhs . is_int32 ( ) | | lhs . is_object ( ) | | lhs . is_boolean ( ) | | lhs . is_nullish ( ) )
return lhs . encoded ( ) = = rhs . encoded ( ) ;
}
return is_strictly_equal ( lhs , rhs ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_strictly_equals ( VM * vm , u32 pc , Op : : StrictlyEquals const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , Value { strictly_equals ( vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) } ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : StrictlyEquals ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_strictly_inequals ( VM * vm , u32 pc , Op : : StrictlyInequals const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , Value { ! strictly_equals ( vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) } ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : StrictlyInequals ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_loosely_equals ( VM * vm , u32 pc , Op : : LooselyEquals const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , Value { ASM_TRY ( * vm , pc , loosely_equals ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) } ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : LooselyEquals ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_loosely_inequals ( VM * vm , u32 pc , Op : : LooselyInequals const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , Value { ASM_TRY ( * vm , pc , loosely_inequals ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) } ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : LooselyInequals ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_unary_minus ( VM * vm , u32 pc , Op : : UnaryMinus const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , unary_minus ( * vm , vm - > get ( instruction - > src ( ) ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : UnaryMinus ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_to_string ( VM * vm , u32 pc , Op : : ToString const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto result = ASM_TRY ( * vm , pc , vm - > get ( instruction - > value ( ) ) . to_primitive_string ( * vm ) ) ;
vm - > set ( instruction - > dst ( ) , Value { result } ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : ToString ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_to_primitive_with_string_hint ( VM * vm , u32 pc , Op : : ToPrimitiveWithStringHint const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto result = ASM_TRY ( * vm , pc , vm - > get ( instruction - > value ( ) ) . to_primitive ( * vm , Value : : PreferredType : : String ) ) ;
vm - > set ( instruction - > dst ( ) , result ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : ToPrimitiveWithStringHint ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_to_object ( VM * vm , u32 pc , Op : : ToObject const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto result = ASM_TRY ( * vm , pc , vm - > get ( instruction - > value ( ) ) . to_object ( * vm ) ) ;
vm - > set ( instruction - > dst ( ) , result ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : ToObject ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_to_length ( VM * vm , u32 pc , Op : : ToLength const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto result = ASM_TRY ( * vm , pc , vm - > get ( instruction - > value ( ) ) . to_length ( * vm ) ) ;
vm - > set ( instruction - > dst ( ) , Value { result } ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : ToLength ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_typeof ( VM * vm , u32 pc , Op : : Typeof const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , vm - > get ( instruction - > src ( ) ) . typeof_ ( * vm ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : Typeof ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_postfix_decrement ( VM * vm , u32 pc , Op : : PostfixDecrement const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto old_value = ASM_TRY ( * vm , pc , vm - > get ( instruction - > src ( ) ) . to_numeric ( * vm ) ) ;
vm - > set ( instruction - > dst ( ) , old_value ) ;
2026-06-14 11:07:00 -03:00
if ( old_value . is_number ( ) )
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > src ( ) , Value ( old_value . as_double ( ) - 1 ) ) ;
2026-06-14 11:07:00 -03:00
else
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > src ( ) , BigInt : : create ( * vm , old_value . as_bigint ( ) . big_integer ( ) . minus ( Crypto : : SignedBigInteger { 1 } ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : PostfixDecrement ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_to_int32 ( VM * vm , u32 pc , Op : : ToInt32 const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , Value ( ASM_TRY ( * vm , pc , vm - > get ( instruction - > value ( ) ) . to_i32 ( * vm ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : ToInt32 ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_put_by_value ( VM * vm , u32 pc , Op : : PutByValue const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto value = vm - > get ( instruction - > src ( ) ) ;
auto base = vm - > get ( instruction - > base ( ) ) ;
2026-06-14 11:07:00 -03:00
Optional < Utf16FlyString const & > base_identifier ;
2026-06-14 09:30:56 -03:00
if ( instruction - > base_identifier ( ) . has_value ( ) )
base_identifier = vm - > get_identifier ( instruction - > base_identifier ( ) . value ( ) ) ;
auto property = vm - > get ( instruction - > property ( ) ) ;
2026-06-14 11:07:00 -03:00
auto property_key = ASM_TRY ( * vm , pc , property . to_property_key ( * vm ) ) ;
2026-06-14 09:30:56 -03:00
ASM_TRY ( * vm , pc , put_by_property_key ( * vm , base , base , value , base_identifier , property_key , instruction - > kind ( ) , instruction - > strict ( ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : PutByValue ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_put_by_value_with_this ( VM * vm , u32 pc , Op : : PutByValueWithThis const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto value = vm - > get ( instruction - > src ( ) ) ;
auto base = vm - > get ( instruction - > base ( ) ) ;
auto this_value = vm - > get ( instruction - > this_value ( ) ) ;
auto property_key = ASM_TRY ( * vm , pc , vm - > get ( instruction - > property ( ) ) . to_property_key ( * vm ) ) ;
ASM_TRY ( * vm , pc , put_by_property_key ( * vm , base , this_value , value , { } , property_key , instruction - > kind ( ) , instruction - > strict ( ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : PutByValueWithThis ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_put_by_spread ( VM * vm , u32 pc , Op : : PutBySpread const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto value = vm - > get ( instruction - > src ( ) ) ;
auto base = vm - > get ( instruction - > base ( ) ) ;
2026-06-14 11:07:00 -03:00
// a. Let baseObj be ? ToObject(V.[[Base]]).
auto object = ASM_TRY ( * vm , pc , base . to_object ( * vm ) ) ;
ASM_TRY ( * vm , pc , object - > copy_data_properties ( * vm , value , { } ) ) ;
return static_cast < i64 > ( pc + sizeof ( Op : : PutBySpread ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_try_put_by_value_holey_array ( VM * vm , u32 , Op : : PutByValue const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto base = vm - > get ( instruction - > base ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( ! base . is_object ( ) ) [[unlikely]]
return 1 ;
2026-06-14 09:30:56 -03:00
auto property = vm - > get ( instruction - > property ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( ! property . is_non_negative_int32 ( ) ) [[unlikely]]
return 1 ;
auto & object = base . as_object ( ) ;
if ( ! is < JS : : Array > ( object ) ) [[unlikely]]
return 1 ;
auto & array = static_cast < JS : : Array & > ( object ) ;
if ( array . is_proxy_target ( )
| | ! array . default_prototype_chain_intact ( )
| | ! array . extensible ( )
| | array . may_interfere_with_indexed_property_access ( )
| | array . indexed_storage_kind ( ) ! = IndexedStorageKind : : Holey ) [[unlikely]]
return 1 ;
auto index = static_cast < u32 > ( property . as_i32 ( ) ) ;
if ( index > = array . indexed_array_like_size ( ) ) [[unlikely]]
return 1 ;
2026-06-14 09:30:56 -03:00
array . indexed_put ( index , vm - > get ( instruction - > src ( ) ) ) ;
2026-06-14 11:07:00 -03:00
return 0 ;
}
// Try to inline a JS-to-JS call by building the callee frame through the
// shared VM::push_inline_frame() helper. Returns 0 on success (callee frame
// pushed) and 1 on failure (caller should keep handling the Call itself).
2026-06-14 09:30:56 -03:00
i64 asm_try_inline_call ( VM * vm , u32 pc , Op : : Call const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto callee = vm - > get ( instruction - > callee ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( ! callee . is_object ( ) ) [[unlikely]]
return 1 ;
auto & callee_object = callee . as_object ( ) ;
if ( ! is < ECMAScriptFunctionObject > ( callee_object ) ) [[unlikely]]
return 1 ;
auto & callee_function = static_cast < ECMAScriptFunctionObject & > ( callee_object ) ;
if ( ! callee_function . can_inline_call ( ) ) [[unlikely]]
return 1 ;
auto * callee_context = vm - > push_inline_frame (
callee_function ,
callee_function . inline_call_executable ( ) ,
2026-06-14 09:30:56 -03:00
instruction - > arguments ( ) ,
pc + instruction - > length ( ) ,
instruction - > dst ( ) . raw ( ) ,
vm - > get ( instruction - > this_value ( ) ) ,
2026-06-14 11:07:00 -03:00
nullptr ,
false ) ;
return callee_context ? 0 : 1 ;
}
// Fast cache-only PutById. Tries all cache entries for ChangeOwnProperty and
// AddOwnProperty. Returns 0 on cache hit, 1 on miss (caller should use full slow path).
2026-06-14 09:30:56 -03:00
i64 asm_try_put_by_id_cache ( VM * vm , u32 , Op : : PutById const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto base = vm - > get ( instruction - > base ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( ! base . is_object ( ) ) [[unlikely]]
return 1 ;
auto & object = base . as_object ( ) ;
2026-06-14 09:30:56 -03:00
auto value = vm - > get ( instruction - > src ( ) ) ;
auto & cache = vm - > current_executable ( ) . property_lookup_caches [ instruction - > cache ( ) ] ;
2026-06-14 11:07:00 -03:00
for ( auto & entry : cache . entries ( ) ) {
switch ( entry . type ) {
case PropertyLookupCache : : Entry : : Type : : ChangeOwnProperty : {
auto cached_shape = entry . shape . ptr ( ) ;
if ( cached_shape ! = & object . shape ( ) ) [[unlikely]]
continue ;
if ( cached_shape - > is_dictionary ( )
& & cached_shape - > dictionary_generation ( ) ! = entry . shape_dictionary_generation )
continue ;
auto current = object . get_direct ( entry . property_offset ) ;
if ( current . is_accessor ( ) ) [[unlikely]]
return 1 ;
object . put_direct ( entry . property_offset , value ) ;
return 0 ;
}
case PropertyLookupCache : : Entry : : Type : : AddOwnProperty : {
if ( entry . from_shape ! = & object . shape ( ) ) [[unlikely]]
continue ;
auto cached_shape = entry . shape . ptr ( ) ;
if ( ! cached_shape ) [[unlikely]]
continue ;
if ( ! object . extensible ( ) ) [[unlikely]]
continue ;
if ( cached_shape - > is_dictionary ( )
& & object . shape ( ) . dictionary_generation ( ) ! = entry . shape_dictionary_generation )
continue ;
auto pcv = entry . prototype_chain_validity . ptr ( ) ;
if ( pcv & & ! pcv - > is_valid ( ) ) [[unlikely]]
continue ;
object . unsafe_set_shape ( * cached_shape ) ;
object . put_direct ( entry . property_offset , value ) ;
return 0 ;
}
default :
continue ;
}
}
return 1 ;
}
// Fast cache-only GetById. Tries all cache entries for own-property and prototype
// chain lookups. On cache hit, writes the result to the dst operand and returns 0.
// On miss, returns 1 (caller should use full slow path).
2026-06-14 09:30:56 -03:00
i64 asm_try_get_by_id_cache ( VM * vm , u32 , Op : : GetById const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto base = vm - > get ( instruction - > base ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( ! base . is_object ( ) ) [[unlikely]]
return 1 ;
auto & object = base . as_object ( ) ;
auto & shape = object . shape ( ) ;
2026-06-14 09:30:56 -03:00
auto & cache = vm - > current_executable ( ) . property_lookup_caches [ instruction - > cache ( ) ] ;
2026-06-14 11:07:00 -03:00
for ( auto & entry : cache . entries ( ) ) {
if ( entry . type ! = PropertyLookupCache : : Entry : : Type : : GetOwnProperty
& & entry . type ! = PropertyLookupCache : : Entry : : Type : : GetPropertyInPrototypeChain ) {
continue ;
}
auto cached_prototype = entry . prototype . ptr ( ) ;
if ( cached_prototype ) {
if ( & shape ! = entry . shape ) [[unlikely]]
continue ;
if ( shape . is_dictionary ( )
& & shape . dictionary_generation ( ) ! = entry . shape_dictionary_generation )
continue ;
auto pcv = entry . prototype_chain_validity . ptr ( ) ;
if ( ! pcv | | ! pcv - > is_valid ( ) ) [[unlikely]]
continue ;
auto value = cached_prototype - > get_direct ( entry . property_offset ) ;
if ( value . is_accessor ( ) ) [[unlikely]]
return 1 ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , value ) ;
2026-06-14 11:07:00 -03:00
return 0 ;
} else if ( & shape = = entry . shape ) {
if ( shape . is_dictionary ( )
& & shape . dictionary_generation ( ) ! = entry . shape_dictionary_generation )
continue ;
auto value = object . get_direct ( entry . property_offset ) ;
if ( value . is_accessor ( ) ) [[unlikely]]
return 1 ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , value ) ;
2026-06-14 11:07:00 -03:00
return 0 ;
}
}
return 1 ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_binding ( VM * vm , u32 pc , Op : : GetBinding const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto next_pc = asm_get_binding < AsmBindingIsKnownToBeInitialized : : No > ( * vm , pc , instruction - > dst ( ) , instruction - > cache ( ) ) ;
return advance_or_continue < Op : : GetBinding > ( pc , next_pc ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_dynamic_get_binding ( VM * vm , u32 pc , Op : : DynamicGetBinding const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & cache = vm - > current_executable ( ) . environment_coordinate_caches [ instruction - > cache ( ) ] ;
auto next_pc = asm_dynamic_get_binding < AsmBindingIsKnownToBeInitialized : : No > ( * vm , pc , instruction - > dst ( ) , instruction - > identifier ( ) , instruction - > strict ( ) , cache ) ;
return advance_or_continue < Op : : DynamicGetBinding > ( pc , next_pc ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_initialize_lexical_binding ( VM * vm , u32 pc , Op : : InitializeLexicalBinding const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto next_pc = asm_initialize_or_set_binding < Op : : EnvironmentMode : : Lexical , Op : : BindingInitializationMode : : Initialize > ( * vm , pc , instruction - > strict ( ) , vm - > get ( instruction - > src ( ) ) , instruction - > cache ( ) ) ;
return advance_or_continue < Op : : InitializeLexicalBinding > ( pc , next_pc ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_dynamic_initialize_lexical_binding ( VM * vm , u32 pc , Op : : DynamicInitializeLexicalBinding const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto next_pc = asm_dynamic_initialize_or_set_binding < Op : : EnvironmentMode : : Lexical , Op : : BindingInitializationMode : : Initialize > ( * vm , pc , instruction - > identifier ( ) , instruction - > strict ( ) , vm - > get ( instruction - > src ( ) ) , vm - > current_executable ( ) . environment_coordinate_caches [ instruction - > cache ( ) ] ) ;
return advance_or_continue < Op : : DynamicInitializeLexicalBinding > ( pc , next_pc ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_initialize_variable_binding ( VM * vm , u32 pc , Op : : InitializeVariableBinding const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto next_pc = asm_initialize_or_set_binding < Op : : EnvironmentMode : : Var , Op : : BindingInitializationMode : : Initialize > ( * vm , pc , instruction - > strict ( ) , vm - > get ( instruction - > src ( ) ) , instruction - > cache ( ) ) ;
return advance_or_continue < Op : : InitializeVariableBinding > ( pc , next_pc ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_dynamic_initialize_variable_binding ( VM * vm , u32 pc , Op : : DynamicInitializeVariableBinding const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto next_pc = asm_dynamic_initialize_or_set_binding < Op : : EnvironmentMode : : Var , Op : : BindingInitializationMode : : Initialize > ( * vm , pc , instruction - > identifier ( ) , instruction - > strict ( ) , vm - > get ( instruction - > src ( ) ) , vm - > current_executable ( ) . environment_coordinate_caches [ instruction - > cache ( ) ] ) ;
return advance_or_continue < Op : : DynamicInitializeVariableBinding > ( pc , next_pc ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_set_lexical_binding ( VM * vm , u32 pc , Op : : SetLexicalBinding const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto next_pc = asm_initialize_or_set_binding < Op : : EnvironmentMode : : Lexical , Op : : BindingInitializationMode : : Set > ( * vm , pc , instruction - > strict ( ) , vm - > get ( instruction - > src ( ) ) , instruction - > cache ( ) ) ;
return advance_or_continue < Op : : SetLexicalBinding > ( pc , next_pc ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_dynamic_set_lexical_binding ( VM * vm , u32 pc , Op : : DynamicSetLexicalBinding const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto next_pc = asm_dynamic_initialize_or_set_binding < Op : : EnvironmentMode : : Lexical , Op : : BindingInitializationMode : : Set > ( * vm , pc , instruction - > identifier ( ) , instruction - > strict ( ) , vm - > get ( instruction - > src ( ) ) , vm - > current_executable ( ) . environment_coordinate_caches [ instruction - > cache ( ) ] ) ;
return advance_or_continue < Op : : DynamicSetLexicalBinding > ( pc , next_pc ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_set_variable_binding ( VM * vm , u32 pc , Op : : SetVariableBinding const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto next_pc = asm_initialize_or_set_binding < Op : : EnvironmentMode : : Var , Op : : BindingInitializationMode : : Set > ( * vm , pc , instruction - > strict ( ) , vm - > get ( instruction - > src ( ) ) , instruction - > cache ( ) ) ;
return advance_or_continue < Op : : SetVariableBinding > ( pc , next_pc ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_dynamic_set_variable_binding ( VM * vm , u32 pc , Op : : DynamicSetVariableBinding const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto next_pc = asm_dynamic_initialize_or_set_binding < Op : : EnvironmentMode : : Var , Op : : BindingInitializationMode : : Set > ( * vm , pc , instruction - > identifier ( ) , instruction - > strict ( ) , vm - > get ( instruction - > src ( ) ) , vm - > current_executable ( ) . environment_coordinate_caches [ instruction - > cache ( ) ] ) ;
return advance_or_continue < Op : : DynamicSetVariableBinding > ( pc , next_pc ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_resolve_binding ( VM * vm , u32 pc , Op : : ResolveBinding const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto const & identifier = vm - > get_identifier ( instruction - > identifier ( ) ) ;
auto reference = ASM_TRY ( * vm , pc , vm - > resolve_binding ( identifier , instruction - > strict ( ) ) ) ;
2026-06-14 11:07:00 -03:00
if ( reference . is_unresolvable ( ) ) {
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , js_null ( ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : ResolveBinding ) ) ;
}
VERIFY ( reference . is_environment_reference ( ) ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , & reference . base_environment ( ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : ResolveBinding ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_resolve_super_base ( VM * vm , u32 pc , Op : : ResolveSuperBase const * instruction )
2026-06-14 11:07:00 -03:00
{
auto & environment = as < FunctionEnvironment > ( * get_this_environment ( * vm ) ) ;
VERIFY ( environment . has_super_binding ( ) ) ;
auto base_value = ASM_TRY ( * vm , pc , environment . get_super_base ( ) ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , base_value ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : ResolveSuperBase ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_set_resolved_binding ( VM * vm , u32 pc , Op : : SetResolvedBinding const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto const & identifier = vm - > get_identifier ( instruction - > identifier ( ) ) ;
auto environment = vm - > get ( instruction - > environment ( ) ) ;
2026-06-14 11:07:00 -03:00
auto reference = environment . is_null ( )
2026-06-14 09:30:56 -03:00
? Reference { Reference : : BaseType : : Unresolvable , PropertyKey { identifier } , instruction - > strict ( ) }
: Reference { as < Environment > ( environment . as_cell ( ) ) , identifier , instruction - > strict ( ) } ;
ASM_TRY ( * vm , pc , reference . put_value ( * vm , vm - > get ( instruction - > src ( ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : SetResolvedBinding ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_typeof_binding ( VM * vm , u32 pc , Op : : TypeofBinding const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
VERIFY ( instruction - > cache ( ) . is_valid ( ) ) ;
2026-06-14 11:07:00 -03:00
auto const * environment = vm - > running_execution_context ( ) . lexical_environment . ptr ( ) ;
2026-06-14 09:30:56 -03:00
for ( size_t i = 0 ; i < instruction - > cache ( ) . hops ; + + i )
2026-06-14 11:07:00 -03:00
environment = environment - > outer_environment ( ) ;
2026-06-14 09:30:56 -03:00
auto value = ASM_TRY ( * vm , pc , static_cast < DeclarativeEnvironment const & > ( * environment ) . get_binding_value_direct ( * vm , instruction - > cache ( ) . index ) ) ;
vm - > set ( instruction - > dst ( ) , value . typeof_ ( * vm ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : TypeofBinding ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_dynamic_typeof_binding ( VM * vm , u32 pc , Op : : DynamicTypeofBinding const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & cache = vm - > current_executable ( ) . environment_coordinate_caches [ instruction - > cache ( ) ] ;
2026-06-14 11:07:00 -03:00
auto const * current_environment = vm - > running_execution_context ( ) . lexical_environment . ptr ( ) ;
if ( auto const * environment = asm_get_cached_environment ( current_environment , cache ) ) [[likely]] {
auto value = ASM_TRY ( * vm , pc , static_cast < DeclarativeEnvironment const & > ( * environment ) . get_binding_value_direct ( * vm , cache . index ) ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , value . typeof_ ( * vm ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : DynamicTypeofBinding ) ) ;
}
2026-06-14 09:30:56 -03:00
auto reference = ASM_TRY ( * vm , pc , vm - > resolve_binding ( vm - > get_identifier ( instruction - > identifier ( ) ) , instruction - > strict ( ) ) ) ;
2026-06-14 11:07:00 -03:00
if ( reference . is_unresolvable ( ) ) {
2026-06-21 14:03:10 -03:00
vm - > set ( instruction - > dst ( ) , PrimitiveString : : create ( * vm , " undefined " _utf16_fly_string ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : DynamicTypeofBinding ) ) ;
}
asm_update_environment_coordinate_cache ( current_environment , reference , cache ) ;
auto value = ASM_TRY ( * vm , pc , reference . get_value ( * vm ) ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , value . typeof_ ( * vm ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : DynamicTypeofBinding ) ) ;
}
static Optional < StringView > asm_function_name_prefix_to_string ( Op : : FunctionNamePrefix prefix )
{
switch ( prefix ) {
case Op : : FunctionNamePrefix : : None :
return { } ;
case Op : : FunctionNamePrefix : : Get :
return " get " sv ;
case Op : : FunctionNamePrefix : : Set :
return " set " sv ;
}
VERIFY_NOT_REACHED ( ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_has_private_id ( VM * vm , u32 pc , Op : : HasPrivateId const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto base = vm - > get ( instruction - > base ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( ! base . is_object ( ) ) [[unlikely]] {
auto completion = vm - > throw_completion < TypeError > ( ErrorType : : InOperatorWithObject ) ;
return handle_asm_exception ( * vm , pc , completion . value ( ) ) ;
}
auto private_environment = vm - > running_execution_context ( ) . private_environment ;
VERIFY ( private_environment ) ;
2026-06-14 09:30:56 -03:00
auto private_name = private_environment - > resolve_private_identifier ( vm - > get_identifier ( instruction - > property ( ) ) ) ;
vm - > set ( instruction - > dst ( ) , Value ( base . as_object ( ) . private_element_find ( private_name ) ! = nullptr ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : HasPrivateId ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_set_function_name ( VM * vm , u32 pc , Op : : SetFunctionName const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto function = vm - > get ( instruction - > function ( ) ) . as_if < ECMAScriptFunctionObject > ( ) ;
2026-06-14 11:07:00 -03:00
if ( ! function | | ! function - > name ( ) . is_empty ( ) )
return static_cast < i64 > ( pc + sizeof ( Op : : SetFunctionName ) ) ;
2026-06-14 09:30:56 -03:00
auto property_key = ASM_TRY ( * vm , pc , vm - > get ( instruction - > name ( ) ) . to_property_key ( * vm ) ) ;
function - > set_inferred_name ( Variant < PropertyKey , PrivateName > { move ( property_key ) } , asm_function_name_prefix_to_string ( instruction - > prefix ( ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : SetFunctionName ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_new_array_with_length ( VM * vm , u32 pc , Op : : NewArrayWithLength const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto length = static_cast < u64 > ( vm - > get ( instruction - > array_length ( ) ) . as_double ( ) ) ;
2026-06-14 11:07:00 -03:00
auto array = ASM_TRY ( * vm , pc , JS : : Array : : create ( vm - > realm ( ) , length ) ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , array ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : NewArrayWithLength ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_array_append ( VM * vm , u32 pc , Op : : ArrayAppend const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto rhs = vm - > get ( instruction - > src ( ) ) ;
auto & lhs_array = vm - > get ( instruction - > dst ( ) ) . as_array_exotic_object ( ) ;
2026-06-14 11:07:00 -03:00
auto lhs_size = lhs_array . indexed_array_like_size ( ) ;
2026-06-14 09:30:56 -03:00
if ( instruction - > is_spread ( ) ) {
2026-06-14 11:07:00 -03:00
size_t i = lhs_size ;
auto result = get_iterator_values ( * vm , rhs , [ & i , & lhs_array ] ( Value iterator_value ) - > Optional < Completion > {
lhs_array . indexed_put ( i , iterator_value ) ;
+ + i ;
return { } ;
} ) ;
if ( result . is_error ( ) ) [[unlikely]]
return handle_asm_exception ( * vm , pc , result . value ( ) ) ;
} else {
lhs_array . indexed_put ( lhs_size , rhs ) ;
}
return static_cast < i64 > ( pc + sizeof ( Op : : ArrayAppend ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_create_variable ( VM * vm , u32 pc , Op : : CreateVariable const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto const & name = vm - > get_identifier ( instruction - > identifier ( ) ) ;
ASM_TRY ( * vm , pc , asm_create_variable ( * vm , name , instruction - > mode ( ) , instruction - > is_global ( ) , instruction - > is_immutable ( ) , instruction - > is_strict ( ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : CreateVariable ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_enter_object_environment ( VM * vm , u32 pc , Op : : EnterObjectEnvironment const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto object = ASM_TRY ( * vm , pc , vm - > get ( instruction - > object ( ) ) . to_object ( * vm ) ) ;
2026-06-14 11:07:00 -03:00
auto & old_environment = vm - > running_execution_context ( ) . lexical_environment ;
auto new_environment = new_object_environment ( * object , true , old_environment ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , new_environment ) ;
2026-06-14 11:07:00 -03:00
vm - > running_execution_context ( ) . lexical_environment = new_environment ;
return static_cast < i64 > ( pc + sizeof ( Op : : EnterObjectEnvironment ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_bitwise_not ( VM * vm , u32 pc , Op : : BitwiseNot const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , bitwise_not ( * vm , vm - > get ( instruction - > src ( ) ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : BitwiseNot ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_unary_plus ( VM * vm , u32 pc , Op : : UnaryPlus const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , ASM_TRY ( * vm , pc , unary_plus ( * vm , vm - > get ( instruction - > src ( ) ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : UnaryPlus ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_is_constructor ( VM * vm , u32 pc , Op : : IsConstructor const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , Value ( vm - > get ( instruction - > value ( ) ) . is_constructor ( ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : IsConstructor ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_add_private_name ( VM * vm , u32 pc , Op : : AddPrivateName const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto const & name = vm - > get_identifier ( instruction - > name ( ) ) ;
2026-06-14 11:07:00 -03:00
vm - > running_execution_context ( ) . private_environment - > add_private_name ( name ) ;
return static_cast < i64 > ( pc + sizeof ( Op : : AddPrivateName ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_create_async_from_sync_iterator ( VM * vm , u32 pc , Op : : CreateAsyncFromSyncIterator const * instruction )
2026-06-14 11:07:00 -03:00
{
auto & realm = vm - > realm ( ) ;
2026-06-14 09:30:56 -03:00
auto & iterator = vm - > get ( instruction - > iterator ( ) ) . as_object ( ) ;
auto next_method = vm - > get ( instruction - > next_method ( ) ) ;
auto done = vm - > get ( instruction - > done ( ) ) . as_bool ( ) ;
2026-06-14 11:07:00 -03:00
auto iterator_record = realm . create < IteratorRecord > ( iterator , next_method , done ) ;
auto async_from_sync_iterator = create_async_from_sync_iterator ( * vm , iterator_record ) ;
auto iterator_object = Object : : create ( realm , nullptr ) ;
iterator_object - > define_direct_property ( vm - > names . iterator , async_from_sync_iterator . iterator , default_attributes ) ;
iterator_object - > define_direct_property ( vm - > names . nextMethod , async_from_sync_iterator . next_method , default_attributes ) ;
iterator_object - > define_direct_property ( vm - > names . done , Value { async_from_sync_iterator . done } , default_attributes ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , iterator_object ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : CreateAsyncFromSyncIterator ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_create_data_property_or_throw ( VM * vm , u32 pc , Op : : CreateDataPropertyOrThrow const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & object = vm - > get ( instruction - > object ( ) ) . as_object ( ) ;
auto property = ASM_TRY ( * vm , pc , vm - > get ( instruction - > property ( ) ) . to_property_key ( * vm ) ) ;
auto value = vm - > get ( instruction - > value ( ) ) ;
2026-06-14 11:07:00 -03:00
ASM_TRY ( * vm , pc , object . create_data_property_or_throw ( property , value ) ) ;
return static_cast < i64 > ( pc + sizeof ( Op : : CreateDataPropertyOrThrow ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_create_immutable_binding ( VM * vm , u32 pc , Op : : CreateImmutableBinding const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & environment = as < Environment > ( vm - > get ( instruction - > environment ( ) ) . as_cell ( ) ) ;
ASM_TRY ( * vm , pc , environment . create_immutable_binding ( * vm , vm - > get_identifier ( instruction - > identifier ( ) ) , instruction - > strict_binding ( ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : CreateImmutableBinding ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_create_mutable_binding ( VM * vm , u32 pc , Op : : CreateMutableBinding const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & environment = as < Environment > ( vm - > get ( instruction - > environment ( ) ) . as_cell ( ) ) ;
ASM_TRY ( * vm , pc , environment . create_mutable_binding ( * vm , vm - > get_identifier ( instruction - > identifier ( ) ) , instruction - > can_be_deleted ( ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : CreateMutableBinding ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_create_rest_params ( VM * vm , u32 pc , Op : : CreateRestParams const * instruction )
2026-06-14 11:07:00 -03:00
{
auto const arguments = vm - > running_execution_context ( ) . arguments_span ( ) ;
auto arguments_count = vm - > running_execution_context ( ) . passed_argument_count ;
auto array = MUST ( JS : : Array : : create ( vm - > realm ( ) , 0 ) ) ;
2026-06-14 09:30:56 -03:00
for ( size_t rest_index = instruction - > rest_index ( ) ; rest_index < arguments_count ; + + rest_index )
2026-06-14 11:07:00 -03:00
array - > indexed_append ( arguments [ rest_index ] ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , array ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : CreateRestParams ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_create_arguments ( VM * vm , u32 pc , Op : : CreateArguments const * instruction )
2026-06-14 11:07:00 -03:00
{
auto const & function = vm - > running_execution_context ( ) . function ;
auto const arguments = vm - > running_execution_context ( ) . arguments_span ( ) ;
auto const & environment = vm - > running_execution_context ( ) . lexical_environment ;
auto passed_arguments = ReadonlySpan < Value > { arguments . data ( ) , vm - > running_execution_context ( ) . passed_argument_count } ;
Object * arguments_object ;
2026-06-14 09:30:56 -03:00
if ( instruction - > kind ( ) = = Op : : ArgumentsKind : : Mapped ) {
2026-06-14 11:07:00 -03:00
auto const & ecma_function = static_cast < ECMAScriptFunctionObject const & > ( * function ) ;
arguments_object = create_mapped_arguments_object ( * vm , * function , ecma_function . parameter_names_for_mapped_arguments ( ) , passed_arguments , * environment ) ;
} else {
arguments_object = create_unmapped_arguments_object ( * vm , passed_arguments ) ;
}
2026-06-14 09:30:56 -03:00
if ( instruction - > dst ( ) . has_value ( ) ) {
vm - > set ( * instruction - > dst ( ) , arguments_object ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : CreateArguments ) ) ;
}
2026-06-14 09:30:56 -03:00
if ( instruction - > is_immutable ( ) ) {
2026-06-14 11:07:00 -03:00
MUST ( environment - > create_immutable_binding ( * vm , vm - > names . arguments . as_string ( ) , false ) ) ;
} else {
MUST ( environment - > create_mutable_binding ( * vm , vm - > names . arguments . as_string ( ) , false ) ) ;
}
MUST ( environment - > initialize_binding ( * vm , vm - > names . arguments . as_string ( ) , arguments_object , Environment : : InitializeBindingHint : : Normal ) ) ;
return static_cast < i64 > ( pc + sizeof ( Op : : CreateArguments ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_await ( VM * vm , [[maybe_unused]] u32 pc , Op : : Await const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto yielded_value = vm - > get ( instruction - > argument ( ) ) . is_special_empty_value ( ) ? js_undefined ( ) : vm - > get ( instruction - > argument ( ) ) ;
2026-06-14 11:07:00 -03:00
auto & context = vm - > running_execution_context ( ) ;
2026-06-14 09:30:56 -03:00
context . yield_continuation = instruction - > continuation_label ( ) . address ( ) ;
2026-06-14 11:07:00 -03:00
context . yield_is_await = true ;
context . yield_value_is_iterator_result = false ;
vm - > do_return ( yielded_value ) ;
return - 1 ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_create_lexical_environment ( VM * vm , u32 pc , Op : : CreateLexicalEnvironment const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & parent = as < Environment > ( vm - > get ( instruction - > parent ( ) ) . as_cell ( ) ) ;
2026-06-14 11:07:00 -03:00
auto environment = new_declarative_environment ( parent ) ;
2026-06-14 09:30:56 -03:00
environment - > ensure_capacity ( instruction - > capacity ( ) ) ;
environment - > set_is_catch_environment ( instruction - > is_catch_environment ( ) ) ;
vm - > set ( instruction - > dst ( ) , environment ) ;
2026-06-14 11:07:00 -03:00
vm - > running_execution_context ( ) . lexical_environment = environment ;
return static_cast < i64 > ( pc + sizeof ( Op : : CreateLexicalEnvironment ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_create_private_environment ( VM * vm , u32 pc , Op : : CreatePrivateEnvironment const * )
2026-06-14 11:07:00 -03:00
{
auto & running_execution_context = vm - > running_execution_context ( ) ;
auto outer_private_environment = running_execution_context . private_environment ;
running_execution_context . private_environment = new_private_environment ( * vm , outer_private_environment ) ;
return static_cast < i64 > ( pc + sizeof ( Op : : CreatePrivateEnvironment ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_create_variable_environment ( VM * vm , u32 pc , Op : : CreateVariableEnvironment const * instruction )
2026-06-14 11:07:00 -03:00
{
auto & running_execution_context = vm - > running_execution_context ( ) ;
auto var_environment = new_declarative_environment ( * running_execution_context . lexical_environment ) ;
2026-06-14 09:30:56 -03:00
if ( auto * shared_data = vm - > active_shared_function_data ( ) ; shared_data & & instruction - > capacity ( ) = = shared_data - > m_var_environment_bindings_count )
var_environment - > set_environment_shape_cache ( shared_data - > m_var_environment_shape , instruction - > capacity ( ) ) ;
var_environment - > ensure_capacity ( instruction - > capacity ( ) ) ;
2026-06-14 11:07:00 -03:00
running_execution_context . variable_environment = var_environment ;
running_execution_context . lexical_environment = var_environment ;
return static_cast < i64 > ( pc + sizeof ( Op : : CreateVariableEnvironment ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_delete_by_id ( VM * vm , u32 pc , Op : : DeleteById const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto const & property_key = vm - > get_property_key ( instruction - > property ( ) ) ;
auto reference = Reference { vm - > get ( instruction - > base ( ) ) , property_key , { } , instruction - > strict ( ) } ;
2026-06-14 11:07:00 -03:00
auto result = ASM_TRY ( * vm , pc , reference . delete_ ( * vm ) ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , Value ( result ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : DeleteById ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_delete_by_value ( VM * vm , u32 pc , Op : : DeleteByValue const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto property_key = ASM_TRY ( * vm , pc , vm - > get ( instruction - > property ( ) ) . to_property_key ( * vm ) ) ;
auto reference = Reference { vm - > get ( instruction - > base ( ) ) , property_key , { } , instruction - > strict ( ) } ;
2026-06-14 11:07:00 -03:00
auto result = ASM_TRY ( * vm , pc , reference . delete_ ( * vm ) ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , Value ( result ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : DeleteByValue ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_delete_variable ( VM * vm , u32 pc , Op : : DeleteVariable const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto const & string = vm - > get_identifier ( instruction - > identifier ( ) ) ;
auto reference = ASM_TRY ( * vm , pc , vm - > resolve_binding ( string , instruction - > strict ( ) ) ) ;
2026-06-14 11:07:00 -03:00
auto result = ASM_TRY ( * vm , pc , reference . delete_ ( * vm ) ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , Value ( result ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : DeleteVariable ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_completion_fields ( VM * vm , u32 pc , Op : : GetCompletionFields const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & completion_source = vm - > get ( instruction - > completion ( ) ) . as_object ( ) ;
2026-06-14 11:07:00 -03:00
if ( is < GeneratorObject > ( completion_source ) ) {
auto const & generator = as < GeneratorObject > ( completion_source ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > value_dst ( ) , generator . pending_completion_value ( ) ) ;
vm - > set ( instruction - > type_dst ( ) , Value ( to_underlying ( generator . pending_completion_type ( ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetCompletionFields ) ) ;
}
auto const & async_generator = as < AsyncGenerator > ( completion_source ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > value_dst ( ) , async_generator . pending_completion_value ( ) ) ;
vm - > set ( instruction - > type_dst ( ) , Value ( to_underlying ( async_generator . pending_completion_type ( ) ) ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetCompletionFields ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_set_completion_type ( VM * vm , u32 pc , Op : : SetCompletionType const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & completion_source = vm - > get ( instruction - > completion ( ) ) . as_object ( ) ;
2026-06-14 11:07:00 -03:00
if ( is < GeneratorObject > ( completion_source ) ) {
2026-06-14 09:30:56 -03:00
as < GeneratorObject > ( completion_source ) . set_pending_completion_type ( instruction - > completion_type ( ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : SetCompletionType ) ) ;
}
2026-06-14 09:30:56 -03:00
as < AsyncGenerator > ( completion_source ) . set_pending_completion_type ( instruction - > completion_type ( ) ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : SetCompletionType ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_template_object ( VM * vm , u32 pc , Op : : GetTemplateObject const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & cache = * vm - > current_executable ( ) . template_object_caches [ instruction - > cache ( ) ] ;
2026-06-14 11:07:00 -03:00
if ( cache . cached_template_object ) {
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , cache . cached_template_object ) ;
return static_cast < i64 > ( pc + instruction - > length ( ) ) ;
2026-06-14 11:07:00 -03:00
}
auto & realm = * vm - > current_realm ( ) ;
2026-06-14 09:30:56 -03:00
auto strings = instruction - > strings ( ) ;
u32 count = instruction - > strings_count ( ) / 2 ;
2026-06-14 11:07:00 -03:00
auto template_object = MUST ( JS : : Array : : create ( realm , count ) ) ;
auto raw_object = MUST ( JS : : Array : : create ( realm , count ) ) ;
for ( size_t index = 0 ; index < count ; + + index ) {
template_object - > indexed_put ( index , vm - > get ( strings [ index ] ) , Attribute : : Enumerable ) ;
raw_object - > indexed_put ( index , vm - > get ( strings [ count + index ] ) , Attribute : : Enumerable ) ;
}
MUST ( raw_object - > set_integrity_level ( Object : : IntegrityLevel : : Frozen ) ) ;
template_object - > define_direct_property ( vm - > names . raw , raw_object , PropertyAttributes { } ) ;
MUST ( template_object - > set_integrity_level ( Object : : IntegrityLevel : : Frozen ) ) ;
cache . cached_template_object = template_object ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , template_object ) ;
return static_cast < i64 > ( pc + instruction - > length ( ) ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_new_function ( VM * vm , u32 pc , Op : : NewFunction const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto & shared_data = * vm - > current_executable ( ) . shared_function_data [ instruction - > shared_function_data_index ( ) ] ;
2026-06-14 11:07:00 -03:00
auto & realm = * vm - > current_realm ( ) ;
GC : : Ref < Object > prototype = [ & ] ( ) - > GC : : Ref < Object > {
switch ( shared_data . m_kind ) {
case FunctionKind : : Normal :
return realm . intrinsics ( ) . function_prototype ( ) ;
case FunctionKind : : Generator :
return realm . intrinsics ( ) . generator_function_prototype ( ) ;
case FunctionKind : : Async :
return realm . intrinsics ( ) . async_function_prototype ( ) ;
case FunctionKind : : AsyncGenerator :
return realm . intrinsics ( ) . async_generator_function_prototype ( ) ;
}
VERIFY_NOT_REACHED ( ) ;
} ( ) ;
auto function = ECMAScriptFunctionObject : : create_from_function_data (
realm ,
shared_data ,
vm - > lexical_environment ( ) ,
vm - > running_execution_context ( ) . private_environment ,
* prototype ) ;
2026-06-14 09:30:56 -03:00
if ( instruction - > home_object ( ) . has_value ( ) ) {
auto home_object_value = vm - > get ( instruction - > home_object ( ) . value ( ) ) ;
2026-06-14 11:07:00 -03:00
function - > make_method ( home_object_value . as_object ( ) ) ;
}
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , function ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : NewFunction ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_throw ( VM * vm , u32 pc , Op : : Throw const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
return handle_asm_exception ( * vm , pc , vm - > get ( instruction - > src ( ) ) ) ;
2026-06-14 11:07:00 -03:00
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_throw_if_tdz ( VM * vm , u32 pc , Op : : ThrowIfTDZ const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto value = vm - > get ( instruction - > src ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( value . is_special_empty_value ( ) ) [[unlikely]] {
auto completion = vm - > throw_completion < ReferenceError > ( ErrorType : : BindingNotInitialized , value ) ;
return handle_asm_exception ( * vm , pc , completion . value ( ) ) ;
}
return static_cast < i64 > ( pc + sizeof ( Op : : ThrowIfTDZ ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_throw_if_not_object ( VM * vm , u32 pc , Op : : ThrowIfNotObject const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto src = vm - > get ( instruction - > src ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( ! src . is_object ( ) ) [[unlikely]] {
auto completion = vm - > throw_completion < TypeError > ( ErrorType : : NotAnObject , src ) ;
return handle_asm_exception ( * vm , pc , completion . value ( ) ) ;
}
return static_cast < i64 > ( pc + sizeof ( Op : : ThrowIfNotObject ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_throw_if_nullish ( VM * vm , u32 pc , Op : : ThrowIfNullish const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto value = vm - > get ( instruction - > src ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( value . is_nullish ( ) ) [[unlikely]] {
auto completion = vm - > throw_completion < TypeError > ( ErrorType : : NotObjectCoercible , value ) ;
return handle_asm_exception ( * vm , pc , completion . value ( ) ) ;
}
return static_cast < i64 > ( pc + sizeof ( Op : : ThrowIfNullish ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_throw_const_assignment ( VM * vm , u32 pc , Op : : ThrowConstAssignment const * )
2026-06-14 11:07:00 -03:00
{
auto completion = vm - > throw_completion < TypeError > ( ErrorType : : InvalidAssignToConst ) ;
return handle_asm_exception ( * vm , pc , completion . value ( ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_yield ( VM * vm , [[maybe_unused]] u32 pc , Op : : Yield const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto yielded_value = vm - > get ( instruction - > value ( ) ) . is_special_empty_value ( ) ? js_undefined ( ) : vm - > get ( instruction - > value ( ) ) ;
2026-06-14 11:07:00 -03:00
auto & context = vm - > running_execution_context ( ) ;
2026-06-14 09:30:56 -03:00
if ( instruction - > continuation_label ( ) . has_value ( ) )
context . yield_continuation = instruction - > continuation_label ( ) - > address ( ) ;
2026-06-14 11:07:00 -03:00
else
context . yield_continuation = ExecutionContext : : no_yield_continuation ;
context . yield_is_await = false ;
context . yield_value_is_iterator_result = false ;
vm - > do_return ( yielded_value ) ;
return - 1 ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_yield_iterator_result ( VM * vm , [[maybe_unused]] u32 pc , Op : : YieldIteratorResult const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto yielded_value = vm - > get ( instruction - > value ( ) ) . is_special_empty_value ( ) ? js_undefined ( ) : vm - > get ( instruction - > value ( ) ) ;
2026-06-14 11:07:00 -03:00
auto & context = vm - > running_execution_context ( ) ;
2026-06-14 09:30:56 -03:00
context . yield_continuation = instruction - > continuation_label ( ) . address ( ) ;
2026-06-14 11:07:00 -03:00
context . yield_is_await = false ;
context . yield_value_is_iterator_result = true ;
vm - > do_return ( yielded_value ) ;
return - 1 ;
}
// Fast path for GetByValue on typed arrays.
// Returns 0 on success (result stored in dst), 1 on miss (fall to slow path).
2026-06-14 09:30:56 -03:00
i64 asm_try_get_by_value_typed_array ( VM * vm , u32 , Op : : GetByValue const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto base = vm - > get ( instruction - > base ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( ! base . is_object ( ) ) [[unlikely]]
return 1 ;
2026-06-14 09:30:56 -03:00
auto property = vm - > get ( instruction - > property ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( ! property . is_non_negative_int32 ( ) ) [[unlikely]]
return 1 ;
auto & object = base . as_object ( ) ;
if ( ! object . is_typed_array ( ) ) [[unlikely]]
return 1 ;
auto & typed_array = static_cast < TypedArrayBase & > ( object ) ;
auto index = static_cast < u32 > ( property . as_i32 ( ) ) ;
// Fast path: fixed-length typed array with cached data pointer
auto const & array_length = typed_array . array_length ( ) ;
if ( array_length . is_auto ( ) ) [[unlikely]]
return 1 ;
auto length = array_length . length ( ) ;
if ( index > = length ) [[unlikely]] {
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , js_undefined ( ) ) ;
2026-06-14 11:07:00 -03:00
return 0 ;
}
if ( ! is_valid_integer_index ( typed_array , CanonicalIndex { CanonicalIndex : : Type : : Index , index } ) ) [[unlikely]] {
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , js_undefined ( ) ) ;
2026-06-14 11:07:00 -03:00
return 0 ;
}
auto * buffer = typed_array . viewed_array_buffer ( ) ;
auto const * data = buffer - > data ( ) + typed_array . byte_offset ( ) ;
Value result ;
switch ( typed_array . kind ( ) ) {
case TypedArrayBase : : Kind : : Uint8Array :
case TypedArrayBase : : Kind : : Uint8ClampedArray :
result = Value ( static_cast < i32 > ( data [ index ] ) ) ;
break ;
case TypedArrayBase : : Kind : : Int8Array :
result = Value ( static_cast < i32 > ( reinterpret_cast < i8 const * > ( data ) [ index ] ) ) ;
break ;
case TypedArrayBase : : Kind : : Uint16Array :
result = Value ( static_cast < i32 > ( reinterpret_cast < u16 const * > ( data ) [ index ] ) ) ;
break ;
case TypedArrayBase : : Kind : : Int16Array :
result = Value ( static_cast < i32 > ( reinterpret_cast < i16 const * > ( data ) [ index ] ) ) ;
break ;
case TypedArrayBase : : Kind : : Uint32Array :
result = Value ( static_cast < double > ( reinterpret_cast < u32 const * > ( data ) [ index ] ) ) ;
break ;
case TypedArrayBase : : Kind : : Int32Array :
result = Value ( reinterpret_cast < i32 const * > ( data ) [ index ] ) ;
break ;
case TypedArrayBase : : Kind : : Float32Array :
result = Value ( static_cast < double > ( reinterpret_cast < float const * > ( data ) [ index ] ) ) ;
break ;
case TypedArrayBase : : Kind : : Float64Array :
result = Value ( reinterpret_cast < double const * > ( data ) [ index ] ) ;
break ;
default :
return 1 ;
}
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , result ) ;
2026-06-14 11:07:00 -03:00
return 0 ;
}
// Fast path for PutByValue on typed arrays.
// Returns 0 on success, 1 on miss (fall to slow path).
2026-06-14 09:30:56 -03:00
i64 asm_try_put_by_value_typed_array ( VM * vm , u32 , Op : : PutByValue const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto base = vm - > get ( instruction - > base ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( ! base . is_object ( ) ) [[unlikely]]
return 1 ;
2026-06-14 09:30:56 -03:00
auto property = vm - > get ( instruction - > property ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( ! property . is_non_negative_int32 ( ) ) [[unlikely]]
return 1 ;
auto & object = base . as_object ( ) ;
if ( ! object . is_typed_array ( ) ) [[unlikely]]
return 1 ;
auto & typed_array = static_cast < TypedArrayBase & > ( object ) ;
auto index = static_cast < u32 > ( property . as_i32 ( ) ) ;
auto const & array_length = typed_array . array_length ( ) ;
if ( array_length . is_auto ( ) ) [[unlikely]]
return 1 ;
2026-06-17 07:33:24 -03:00
// NB: An out-of-bounds write is not simply a no-op: TypedArraySetElement still
// evaluates ToNumber(value) for its side effects before discarding the store.
// Fall back to the slow path so those side effects happen.
2026-06-14 11:07:00 -03:00
if ( index > = array_length . length ( ) ) [[unlikely]]
2026-06-17 07:33:24 -03:00
return 1 ;
2026-06-14 11:07:00 -03:00
if ( ! is_valid_integer_index ( typed_array , CanonicalIndex { CanonicalIndex : : Type : : Index , index } ) ) [[unlikely]]
2026-06-17 07:33:24 -03:00
return 1 ;
2026-06-14 11:07:00 -03:00
auto * buffer = typed_array . viewed_array_buffer ( ) ;
auto * data = buffer - > data ( ) + typed_array . byte_offset ( ) ;
2026-06-14 09:30:56 -03:00
auto value = vm - > get ( instruction - > src ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( value . is_int32 ( ) ) {
auto int_val = value . as_i32 ( ) ;
switch ( typed_array . kind ( ) ) {
case TypedArrayBase : : Kind : : Uint8Array :
data [ index ] = static_cast < u8 > ( int_val ) ;
return 0 ;
case TypedArrayBase : : Kind : : Uint8ClampedArray :
data [ index ] = static_cast < u8 > ( clamp ( int_val , 0 , 255 ) ) ;
return 0 ;
case TypedArrayBase : : Kind : : Int8Array :
reinterpret_cast < i8 * > ( data ) [ index ] = static_cast < i8 > ( int_val ) ;
return 0 ;
case TypedArrayBase : : Kind : : Uint16Array :
reinterpret_cast < u16 * > ( data ) [ index ] = static_cast < u16 > ( int_val ) ;
return 0 ;
case TypedArrayBase : : Kind : : Int16Array :
reinterpret_cast < i16 * > ( data ) [ index ] = static_cast < i16 > ( int_val ) ;
return 0 ;
case TypedArrayBase : : Kind : : Uint32Array :
reinterpret_cast < u32 * > ( data ) [ index ] = static_cast < u32 > ( int_val ) ;
return 0 ;
case TypedArrayBase : : Kind : : Int32Array :
reinterpret_cast < i32 * > ( data ) [ index ] = int_val ;
return 0 ;
default :
break ;
}
} else if ( value . is_double ( ) ) {
auto dbl_val = value . as_double ( ) ;
switch ( typed_array . kind ( ) ) {
case TypedArrayBase : : Kind : : Float32Array :
reinterpret_cast < float * > ( data ) [ index ] = static_cast < float > ( dbl_val ) ;
return 0 ;
case TypedArrayBase : : Kind : : Float64Array :
reinterpret_cast < double * > ( data ) [ index ] = dbl_val ;
return 0 ;
default :
break ;
}
}
return 1 ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_instance_of ( VM * vm , u32 pc , Op : : InstanceOf const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto result = ASM_TRY ( * vm , pc , instance_of ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) ;
vm - > set ( instruction - > dst ( ) , result ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : InstanceOf ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_in ( VM * vm , u32 pc , Op : : In const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto result = ASM_TRY ( * vm , pc , in ( * vm , vm - > get ( instruction - > lhs ( ) ) , vm - > get ( instruction - > rhs ( ) ) ) ) ;
vm - > set ( instruction - > dst ( ) , result ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : In ) ) ;
}
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_resolve_this_binding ( VM * vm , u32 pc , Op : : ResolveThisBinding const * )
2026-06-14 11:07:00 -03:00
{
auto & cached_this_value = vm - > reg ( Register : : this_value ( ) ) ;
if ( ! cached_this_value . is_special_empty_value ( ) )
return static_cast < i64 > ( pc + sizeof ( Op : : ResolveThisBinding ) ) ;
auto & running_execution_context = vm - > running_execution_context ( ) ;
if ( auto function = running_execution_context . function ; function & & is < ECMAScriptFunctionObject > ( * function ) ) {
auto & ecmascript_function = static_cast < ECMAScriptFunctionObject & > ( * function ) ;
if ( ! ecmascript_function . allocates_function_environment ( ) & & ! ecmascript_function . this_value_needs_environment_resolution ( ) ) {
cached_this_value = running_execution_context . this_value . value ( ) ;
return static_cast < i64 > ( pc + sizeof ( Op : : ResolveThisBinding ) ) ;
}
}
cached_this_value = ASM_TRY ( * vm , pc , vm - > resolve_this_binding ( ) ) ;
return static_cast < i64 > ( pc + sizeof ( Op : : ResolveThisBinding ) ) ;
}
// Direct handler for GetPrivateById: bypasses Reference indirection.
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_get_private_by_id ( VM * vm , u32 pc , Op : : GetPrivateById const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto base_value = vm - > get ( instruction - > base ( ) ) ;
2026-06-14 11:07:00 -03:00
auto & current_vm = * vm ;
if ( ! base_value . is_object ( ) ) [[unlikely]] {
ASM_TRY ( * vm , pc , base_value . to_object ( current_vm ) ) ;
2026-06-14 09:30:56 -03:00
auto const & name = current_vm . get_identifier ( instruction - > property ( ) ) ;
2026-06-14 11:07:00 -03:00
auto private_name = make_private_reference ( current_vm , base_value , name ) ;
auto result = ASM_TRY ( * vm , pc , private_name . get_value ( current_vm ) ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , result ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetPrivateById ) ) ;
}
2026-06-14 09:30:56 -03:00
auto const & name = current_vm . get_identifier ( instruction - > property ( ) ) ;
2026-06-14 11:07:00 -03:00
auto private_environment = current_vm . running_execution_context ( ) . private_environment ;
VERIFY ( private_environment ) ;
auto private_name = private_environment - > resolve_private_identifier ( name ) ;
auto result = ASM_TRY ( * vm , pc , base_value . as_object ( ) . private_get ( private_name ) ) ;
2026-06-14 09:30:56 -03:00
vm - > set ( instruction - > dst ( ) , result ) ;
2026-06-14 11:07:00 -03:00
return static_cast < i64 > ( pc + sizeof ( Op : : GetPrivateById ) ) ;
}
// Direct handler for PutPrivateById: bypasses Reference indirection.
2026-06-14 09:30:56 -03:00
i64 asm_slow_path_put_private_by_id ( VM * vm , u32 pc , Op : : PutPrivateById const * instruction )
2026-06-14 11:07:00 -03:00
{
2026-06-14 09:30:56 -03:00
auto base_value = vm - > get ( instruction - > base ( ) ) ;
2026-06-14 11:07:00 -03:00
auto & current_vm = * vm ;
2026-06-14 09:30:56 -03:00
auto value = vm - > get ( instruction - > src ( ) ) ;
2026-06-14 11:07:00 -03:00
if ( ! base_value . is_object ( ) ) [[unlikely]] {
auto object = ASM_TRY ( * vm , pc , base_value . to_object ( current_vm ) ) ;
2026-06-14 09:30:56 -03:00
auto const & name = current_vm . get_identifier ( instruction - > property ( ) ) ;
2026-06-14 11:07:00 -03:00
auto private_reference = make_private_reference ( current_vm , object , name ) ;
ASM_TRY ( * vm , pc , private_reference . put_value ( current_vm , value ) ) ;
return static_cast < i64 > ( pc + sizeof ( Op : : PutPrivateById ) ) ;
}
2026-06-14 09:30:56 -03:00
auto const & name = current_vm . get_identifier ( instruction - > property ( ) ) ;
2026-06-14 11:07:00 -03:00
auto private_environment = current_vm . running_execution_context ( ) . private_environment ;
VERIFY ( private_environment ) ;
auto private_name = private_environment - > resolve_private_identifier ( name ) ;
ASM_TRY ( * vm , pc , base_value . as_object ( ) . private_set ( private_name , value ) ) ;
return static_cast < i64 > ( pc + sizeof ( Op : : PutPrivateById ) ) ;
}
// Helper: convert value to boolean (called from asm jump handlers)
// Returns 0 (false) or 1 (true). Never throws.
u64 asm_helper_to_boolean ( u64 encoded_value )
{
auto value = bit_cast < Value > ( encoded_value ) ;
return value . to_boolean ( ) ? 1 : 0 ;
}
u64 asm_helper_math_exp ( u64 encoded_value )
{
auto value = bit_cast < Value > ( encoded_value ) ;
return bit_cast < u64 > ( Value ( : : exp ( value . as_double ( ) ) ) ) ;
}
u64 asm_helper_empty_string ( u64 )
{
return bit_cast < u64 > ( Value ( & VM : : the ( ) . empty_string ( ) ) ) ;
}
i64 asm_helper_handle_raw_native_exception ( u64 encoded_exception )
{
auto & vm = VM : : the ( ) ;
auto & callee_frame = vm . running_execution_context ( ) ;
VERIFY ( callee_frame . caller_frame ) ;
// Raw-native asm calls keep their callee frame off the VM execution
// context stack, so we have to unwind it manually before exception
// dispatch. Match VM::handle_exception()'s inline-frame semantics by
// probing the caller with a PC inside the Call instruction.
auto caller_pc = callee_frame . caller_return_pc ;
vm . unwind_inline_frame_for_exception ( ) ;
return handle_asm_exception ( vm , caller_pc - 1 , bit_cast < Value > ( encoded_exception ) ) ;
}
u64 asm_helper_single_ascii_character_string ( u64 encoded_value )
{
return bit_cast < u64 > ( Value ( & VM : : the ( ) . single_ascii_character_string ( static_cast < u8 > ( encoded_value ) ) ) ) ;
}
u64 asm_helper_single_utf16_code_unit_string ( u64 encoded_value )
{
char16_t code_unit = static_cast < char16_t > ( encoded_value ) ;
return bit_cast < u64 > ( Value ( PrimitiveString : : create ( VM : : the ( ) , Utf16View ( & code_unit , 1 ) ) ) ) ;
}
} // extern "C"