2021-06-04 11:31:13 +02:00
/*
2025-03-25 18:01:35 +00:00
* Copyright ( c ) 2021 - 2025 , Andreas Kling < andreas @ ladybird . org >
2021-06-07 21:13:37 +01:00
* Copyright ( c ) 2021 , Linus Groh < linusg @ serenityos . org >
2021-06-09 18:18:56 +02:00
* Copyright ( c ) 2021 , Gunnar Beutner < gbeutner @ serenityos . org >
2021-06-11 00:36:16 +02:00
* Copyright ( c ) 2021 , Marcin Gasperowicz < xnooga @ gmail . com >
2021-06-04 11:31:13 +02:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2022-09-09 15:23:02 +02:00
# include <AK/Find.h>
2024-05-08 13:08:38 +02:00
# include <AK/Queue.h>
2021-06-04 11:31:13 +02:00
# include <LibJS/AST.h>
# include <LibJS/Bytecode/Generator.h>
# include <LibJS/Bytecode/Instruction.h>
# include <LibJS/Bytecode/Op.h>
# include <LibJS/Bytecode/Register.h>
2021-06-10 22:12:21 +02:00
# include <LibJS/Bytecode/StringTable.h>
2021-07-01 12:24:46 +02:00
# include <LibJS/Runtime/Environment.h>
2022-12-09 18:48:57 +00:00
# include <LibJS/Runtime/ErrorTypes.h>
2026-02-11 00:20:47 +01:00
# include <LibJS/Runtime/PrimitiveString.h>
# include <LibJS/Runtime/SharedFunctionInstanceData.h>
2026-01-22 17:23:19 -08:00
# include <LibJS/Runtime/VM.h>
2025-03-25 18:01:35 +00:00
# include <LibJS/Runtime/ValueInlines.h>
2021-06-04 11:31:13 +02:00
namespace JS {
2024-05-07 21:36:56 +02:00
using namespace JS : : Bytecode ;
2026-02-11 19:45:44 +01:00
static String bigint_literal_to_decimal_string ( BigIntLiteral const & literal )
{
auto const & raw = literal . raw_value ( ) ;
auto integer = [ & ] {
if ( raw [ 0 ] = = ' 0 ' & & raw . length ( ) > = 3 ) {
if ( raw [ 1 ] = = ' x ' | | raw [ 1 ] = = ' X ' )
return MUST ( Crypto : : SignedBigInteger : : from_base ( 16 , raw . substring ( 2 , raw . length ( ) - 3 ) ) ) ;
if ( raw [ 1 ] = = ' o ' | | raw [ 1 ] = = ' O ' )
return MUST ( Crypto : : SignedBigInteger : : from_base ( 8 , raw . substring ( 2 , raw . length ( ) - 3 ) ) ) ;
if ( raw [ 1 ] = = ' b ' | | raw [ 1 ] = = ' B ' )
return MUST ( Crypto : : SignedBigInteger : : from_base ( 2 , raw . substring ( 2 , raw . length ( ) - 3 ) ) ) ;
}
return MUST ( Crypto : : SignedBigInteger : : from_base ( 10 , raw . substring ( 0 , raw . length ( ) - 1 ) ) ) ;
} ( ) ;
return MUST ( integer . to_base ( 10 ) ) ;
}
2024-05-07 21:36:56 +02:00
static ScopedOperand choose_dst ( Bytecode : : Generator & generator , Optional < ScopedOperand > const & preferred_dst )
2024-02-04 08:00:54 +01:00
{
if ( preferred_dst . has_value ( ) )
return preferred_dst . value ( ) ;
2024-05-07 21:36:56 +02:00
return generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ASTNode : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-04 11:31:13 +02:00
{
2026-02-11 22:42:53 +01:00
generator . emit_todo ( class_name ( ) ) ;
return { } ;
2021-06-04 11:31:13 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ScopeNode : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-04 11:31:13 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2023-06-16 16:34:47 +02:00
bool did_create_lexical_environment = false ;
2023-06-25 07:37:24 +02:00
if ( is < BlockStatement > ( * this ) ) {
2022-02-12 19:48:45 +03:30
if ( has_lexical_declarations ( ) ) {
2024-05-11 13:25:08 +02:00
did_create_lexical_environment = generator . emit_block_declaration_instantiation ( * this ) ;
2022-02-12 19:48:45 +03:30
}
} else if ( is < Program > ( * this ) ) {
2023-06-15 12:36:57 +02:00
// GlobalDeclarationInstantiation is handled by the C++ AO.
2022-02-12 19:48:45 +03:30
} else {
2023-06-15 09:14:25 +02:00
// FunctionDeclarationInstantiation is handled by the C++ AO.
2022-02-12 19:48:45 +03:30
}
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > last_result ;
2021-06-09 19:57:18 +02:00
for ( auto & child : children ( ) ) {
2026-02-11 22:42:53 +01:00
auto result = child - > generate_bytecode ( generator ) ;
2025-04-07 15:59:30 +02:00
if ( generator . must_propagate_completion ( ) ) {
2026-02-10 23:25:24 +01:00
if ( result . has_value ( ) ) {
2025-04-07 15:59:30 +02:00
last_result = result ;
2026-02-10 23:25:24 +01:00
if ( ! generator . is_current_block_terminated ( ) ) {
if ( auto completion_reg = generator . current_completion_register ( ) ; completion_reg . has_value ( ) )
generator . emit_mov ( * completion_reg , * result ) ;
}
}
2025-04-07 15:59:30 +02:00
}
2021-06-09 19:57:18 +02:00
if ( generator . is_current_block_terminated ( ) )
break ;
}
2022-02-12 19:54:08 +03:30
2023-06-16 16:34:47 +02:00
if ( did_create_lexical_environment )
2022-02-12 19:48:45 +03:30
generator . end_variable_scope ( ) ;
2024-02-04 08:00:54 +01:00
return last_result ;
2021-06-04 11:31:13 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > EmptyStatement : : generate_bytecode ( Bytecode : : Generator & , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-07 20:47:26 +02:00
{
2024-05-07 21:36:56 +02:00
return Optional < ScopedOperand > { } ;
2021-06-07 20:47:26 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ExpressionStatement : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-04 11:31:13 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2022-02-12 19:54:08 +03:30
return m_expression - > generate_bytecode ( generator ) ;
2021-06-04 11:31:13 +02:00
}
2025-03-25 18:01:35 +00:00
static ThrowCompletionOr < ScopedOperand > constant_fold_unary_expression ( Generator & generator , Value value , UnaryOp op )
{
switch ( op ) {
case UnaryOp : : Minus :
return generator . add_constant ( Value ( - TRY ( value . to_double ( generator . vm ( ) ) ) ) ) ;
case UnaryOp : : Plus :
return generator . add_constant ( Value ( + TRY ( value . to_double ( generator . vm ( ) ) ) ) ) ;
case UnaryOp : : BitwiseNot :
return generator . add_constant ( TRY ( bitwise_not ( generator . vm ( ) , value ) ) ) ;
case UnaryOp : : Not :
return generator . add_constant ( Value ( ! value . to_boolean ( ) ) ) ;
default :
return throw_completion ( js_null ( ) ) ;
}
}
2026-01-06 16:03:23 -08:00
static Optional < ScopedOperand > try_constant_fold_unary_expression ( Generator & generator , ScopedOperand & operand , UnaryOp op )
{
if ( operand . operand ( ) . is_constant ( ) ) {
// OPTIMIZATION: Do some basic constant folding for unary operations on numbers.
auto value = generator . get_constant ( operand ) ;
if ( auto result = constant_fold_unary_expression ( generator , value , op ) ; ! result . is_error ( ) )
return result . release_value ( ) ;
}
return { } ;
}
2024-05-31 21:49:46 +02:00
static ThrowCompletionOr < ScopedOperand > constant_fold_binary_expression ( Generator & generator , Value lhs , Value rhs , BinaryOp m_op )
{
switch ( m_op ) {
case BinaryOp : : Addition :
return generator . add_constant ( TRY ( add ( generator . vm ( ) , lhs , rhs ) ) ) ;
case BinaryOp : : Subtraction :
return generator . add_constant ( TRY ( sub ( generator . vm ( ) , lhs , rhs ) ) ) ;
case BinaryOp : : Multiplication :
return generator . add_constant ( TRY ( mul ( generator . vm ( ) , lhs , rhs ) ) ) ;
case BinaryOp : : Division :
return generator . add_constant ( TRY ( div ( generator . vm ( ) , lhs , rhs ) ) ) ;
case BinaryOp : : Modulo :
return generator . add_constant ( TRY ( mod ( generator . vm ( ) , lhs , rhs ) ) ) ;
case BinaryOp : : Exponentiation :
return generator . add_constant ( TRY ( exp ( generator . vm ( ) , lhs , rhs ) ) ) ;
case BinaryOp : : GreaterThan :
2025-05-08 15:19:35 +12:00
return generator . add_constant ( Value { TRY ( greater_than ( generator . vm ( ) , lhs , rhs ) ) } ) ;
2024-05-31 21:49:46 +02:00
case BinaryOp : : GreaterThanEquals :
2025-05-08 15:19:35 +12:00
return generator . add_constant ( Value { TRY ( greater_than_equals ( generator . vm ( ) , lhs , rhs ) ) } ) ;
2024-05-31 21:49:46 +02:00
case BinaryOp : : LessThan :
2025-05-08 15:19:35 +12:00
return generator . add_constant ( Value { TRY ( less_than ( generator . vm ( ) , lhs , rhs ) ) } ) ;
2024-05-31 21:49:46 +02:00
case BinaryOp : : LessThanEquals :
2025-05-08 15:19:35 +12:00
return generator . add_constant ( Value { TRY ( less_than_equals ( generator . vm ( ) , lhs , rhs ) ) } ) ;
2024-05-31 21:49:46 +02:00
case BinaryOp : : LooselyInequals :
return generator . add_constant ( Value ( ! TRY ( is_loosely_equal ( generator . vm ( ) , lhs , rhs ) ) ) ) ;
case BinaryOp : : LooselyEquals :
return generator . add_constant ( Value ( TRY ( is_loosely_equal ( generator . vm ( ) , lhs , rhs ) ) ) ) ;
case BinaryOp : : StrictlyInequals :
return generator . add_constant ( Value ( ! is_strictly_equal ( lhs , rhs ) ) ) ;
case BinaryOp : : StrictlyEquals :
return generator . add_constant ( Value ( is_strictly_equal ( lhs , rhs ) ) ) ;
case BinaryOp : : BitwiseAnd :
return generator . add_constant ( TRY ( bitwise_and ( generator . vm ( ) , lhs , rhs ) ) ) ;
case BinaryOp : : BitwiseOr :
return generator . add_constant ( TRY ( bitwise_or ( generator . vm ( ) , lhs , rhs ) ) ) ;
case BinaryOp : : BitwiseXor :
return generator . add_constant ( TRY ( bitwise_xor ( generator . vm ( ) , lhs , rhs ) ) ) ;
case BinaryOp : : LeftShift :
return generator . add_constant ( TRY ( left_shift ( generator . vm ( ) , lhs , rhs ) ) ) ;
case BinaryOp : : RightShift :
return generator . add_constant ( TRY ( right_shift ( generator . vm ( ) , lhs , rhs ) ) ) ;
case BinaryOp : : UnsignedRightShift :
return generator . add_constant ( TRY ( unsigned_right_shift ( generator . vm ( ) , lhs , rhs ) ) ) ;
case BinaryOp : : In :
case BinaryOp : : InstanceOf :
// NOTE: We just have to throw *something* to indicate that this is not a constant foldable operation.
return throw_completion ( js_null ( ) ) ;
default :
VERIFY_NOT_REACHED ( ) ;
}
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > BinaryExpression : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2021-06-04 11:31:13 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2023-07-05 12:42:50 +02:00
if ( m_op = = BinaryOp : : In & & is < PrivateIdentifier > ( * m_lhs ) ) {
auto const & private_identifier = static_cast < PrivateIdentifier const & > ( * m_lhs ) . string ( ) ;
2026-02-11 22:42:53 +01:00
auto base = m_rhs - > generate_bytecode ( generator ) . value ( ) ;
2024-02-04 08:00:54 +01:00
auto dst = choose_dst ( generator , preferred_dst ) ;
generator . emit < Bytecode : : Op : : HasPrivateId > ( dst , base , generator . intern_identifier ( private_identifier ) ) ;
return dst ;
2023-07-05 12:42:50 +02:00
}
2024-05-09 15:17:34 +02:00
// OPTIMIZATION: If LHS and/or RHS are numeric literals, we make sure they are converted to i32/u32
// as appropriate, to avoid having to perform these conversions at runtime.
2021-06-04 11:31:13 +02:00
2026-02-11 22:42:53 +01:00
auto get_left_side = [ & ] ( Expression const & side ) - > Optional < ScopedOperand > {
2024-05-09 15:17:34 +02:00
switch ( m_op ) {
case BinaryOp : : BitwiseAnd :
case BinaryOp : : BitwiseOr :
case BinaryOp : : BitwiseXor :
case BinaryOp : : LeftShift :
case BinaryOp : : RightShift :
case BinaryOp : : UnsignedRightShift :
// LHS will always be converted to i32 for these ops.
if ( side . is_numeric_literal ( ) ) {
auto value = MUST ( static_cast < NumericLiteral const & > ( side ) . value ( ) . to_i32 ( generator . vm ( ) ) ) ;
return generator . add_constant ( Value ( value ) ) ;
}
break ;
default :
break ;
}
return side . generate_bytecode ( generator ) ;
} ;
2026-02-11 22:42:53 +01:00
auto get_right_side = [ & ] ( Expression const & side ) - > Optional < ScopedOperand > {
2024-05-09 15:17:34 +02:00
switch ( m_op ) {
case BinaryOp : : BitwiseAnd :
case BinaryOp : : BitwiseOr :
case BinaryOp : : BitwiseXor :
// RHS will always be converted to i32 for these ops.
if ( side . is_numeric_literal ( ) ) {
auto value = MUST ( static_cast < NumericLiteral const & > ( side ) . value ( ) . to_i32 ( generator . vm ( ) ) ) ;
return generator . add_constant ( Value ( value ) ) ;
}
break ;
case BinaryOp : : LeftShift :
case BinaryOp : : RightShift :
case BinaryOp : : UnsignedRightShift :
// RHS will always be converted to u32 for these ops.
if ( side . is_numeric_literal ( ) ) {
auto value = MUST ( static_cast < NumericLiteral const & > ( side ) . value ( ) . to_u32 ( generator . vm ( ) ) ) ;
return generator . add_constant ( Value ( value ) ) ;
}
break ;
default :
break ;
}
return side . generate_bytecode ( generator ) ;
} ;
2026-02-11 22:42:53 +01:00
auto lhs = get_left_side ( * m_lhs ) . value ( ) ;
auto rhs = get_right_side ( * m_rhs ) . value ( ) ;
2024-02-04 08:00:54 +01:00
auto dst = choose_dst ( generator , preferred_dst ) ;
2024-05-31 21:49:46 +02:00
// OPTIMIZATION: Do some basic constant folding for binary operations.
if ( lhs . operand ( ) . is_constant ( ) & & rhs . operand ( ) . is_constant ( ) ) {
if ( auto result = constant_fold_binary_expression ( generator , generator . get_constant ( lhs ) , generator . get_constant ( rhs ) , m_op ) ; ! result . is_error ( ) )
return result . release_value ( ) ;
}
2021-06-04 11:31:13 +02:00
switch ( m_op ) {
case BinaryOp : : Addition :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Add > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-04 11:31:13 +02:00
case BinaryOp : : Subtraction :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Sub > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 19:17:20 +02:00
case BinaryOp : : Multiplication :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Mul > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 19:17:20 +02:00
case BinaryOp : : Division :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Div > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 19:37:23 +02:00
case BinaryOp : : Modulo :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Mod > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 19:37:23 +02:00
case BinaryOp : : Exponentiation :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Exp > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 19:57:38 +02:00
case BinaryOp : : GreaterThan :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : GreaterThan > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 19:57:38 +02:00
case BinaryOp : : GreaterThanEquals :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : GreaterThanEquals > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-04 12:03:57 +02:00
case BinaryOp : : LessThan :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : LessThan > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 19:57:38 +02:00
case BinaryOp : : LessThanEquals :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : LessThanEquals > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-09-24 00:06:10 +02:00
case BinaryOp : : LooselyInequals :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : LooselyInequals > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-09-24 00:06:10 +02:00
case BinaryOp : : LooselyEquals :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : LooselyEquals > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-09-24 00:06:10 +02:00
case BinaryOp : : StrictlyInequals :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : StrictlyInequals > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-09-24 00:06:10 +02:00
case BinaryOp : : StrictlyEquals :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : StrictlyEquals > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 19:16:04 +01:00
case BinaryOp : : BitwiseAnd :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : BitwiseAnd > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 19:16:04 +01:00
case BinaryOp : : BitwiseOr :
2025-12-15 00:14:15 -06:00
if ( rhs . operand ( ) . is_constant ( ) & & generator . get_constant ( rhs ) . is_int32 ( ) & & generator . get_constant ( rhs ) . as_i32 ( ) = = 0 ) {
// OPTIMIZATION: x | 0 == ToInt32(x)
generator . emit < Bytecode : : Op : : ToInt32 > ( dst , lhs ) ;
break ;
}
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : BitwiseOr > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 19:16:04 +01:00
case BinaryOp : : BitwiseXor :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : BitwiseXor > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 20:14:12 +01:00
case BinaryOp : : LeftShift :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : LeftShift > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 20:14:12 +01:00
case BinaryOp : : RightShift :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : RightShift > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 20:14:12 +01:00
case BinaryOp : : UnsignedRightShift :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : UnsignedRightShift > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 21:13:37 +01:00
case BinaryOp : : In :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : In > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 21:18:19 +01:00
case BinaryOp : : InstanceOf :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : InstanceOf > ( dst , lhs , rhs ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-04 11:31:13 +02:00
default :
2021-06-07 21:18:19 +01:00
VERIFY_NOT_REACHED ( ) ;
2021-06-04 11:31:13 +02:00
}
2024-02-04 08:00:54 +01:00
return dst ;
2021-06-04 11:31:13 +02:00
}
2026-02-11 22:42:53 +01:00
static Optional < ScopedOperand > constant_fold_logical_expression ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst , ScopedOperand & lhs , LogicalExpression const * expr )
2026-01-11 12:36:23 -08:00
{
auto constant = generator . get_constant ( lhs ) ;
2026-02-11 22:42:53 +01:00
auto return_rhs = [ & ] - > Optional < ScopedOperand > {
2026-01-11 12:36:23 -08:00
auto dst = choose_dst ( generator , preferred_dst ) ;
2026-02-11 22:42:53 +01:00
auto rhs = expr - > rhs ( ) - > generate_bytecode ( generator , dst ) . value ( ) ;
2026-01-11 12:36:23 -08:00
if ( rhs . operand ( ) . is_constant ( ) )
return rhs ;
generator . emit_mov ( dst , rhs ) ;
return dst ;
} ;
switch ( expr - > op ( ) ) {
case LogicalOp : : And :
if ( constant . to_boolean_slow_case ( ) )
2026-02-11 22:42:53 +01:00
return return_rhs ( ) ;
2026-01-11 12:36:23 -08:00
return lhs ;
case LogicalOp : : Or :
if ( constant . to_boolean_slow_case ( ) )
return lhs ;
2026-02-11 22:42:53 +01:00
return return_rhs ( ) ;
2026-01-11 12:36:23 -08:00
case LogicalOp : : NullishCoalescing :
if ( constant . is_nullish ( ) )
2026-02-11 22:42:53 +01:00
return return_rhs ( ) ;
2026-01-11 12:36:23 -08:00
return lhs ;
default :
VERIFY_NOT_REACHED ( ) ;
}
return Optional < ScopedOperand > { } ;
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > LogicalExpression : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2021-06-08 02:18:47 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2026-02-11 22:42:53 +01:00
auto lhs = m_lhs - > generate_bytecode ( generator , preferred_dst ) . value ( ) ;
2026-01-11 12:36:23 -08:00
// OPTIMIZATION: return lhs/rhs directly if we can detect lhs as a truthy/falsey literal
if ( auto constant = generator . try_get_constant ( lhs ) ; constant . has_value ( ) ) {
2026-02-11 22:42:53 +01:00
return constant_fold_logical_expression ( generator , preferred_dst , lhs , this ) ;
2026-01-11 12:36:23 -08:00
}
2021-06-08 02:18:47 +02:00
2021-06-09 06:49:58 +04:30
// lhs
// jump op (true) end (false) rhs
// rhs
// jump always (true) end
// end
2026-01-11 12:36:23 -08:00
auto dst = choose_dst ( generator , preferred_dst ) ;
generator . emit_mov ( dst , lhs ) ;
2021-06-09 06:49:58 +04:30
auto & rhs_block = generator . make_block ( ) ;
auto & end_block = generator . make_block ( ) ;
2021-06-08 02:18:47 +02:00
switch ( m_op ) {
case LogicalOp : : And :
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
lhs ,
2021-06-09 06:49:58 +04:30
Bytecode : : Label { rhs_block } ,
Bytecode : : Label { end_block } ) ;
2021-06-08 02:18:47 +02:00
break ;
case LogicalOp : : Or :
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
lhs ,
2021-06-09 06:49:58 +04:30
Bytecode : : Label { end_block } ,
Bytecode : : Label { rhs_block } ) ;
2021-06-08 02:18:47 +02:00
break ;
case LogicalOp : : NullishCoalescing :
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : JumpNullish > (
2024-02-04 08:00:54 +01:00
lhs ,
2021-06-09 06:49:58 +04:30
Bytecode : : Label { rhs_block } ,
Bytecode : : Label { end_block } ) ;
2021-06-08 02:18:47 +02:00
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
2021-06-09 06:49:58 +04:30
generator . switch_to_basic_block ( rhs_block ) ;
2025-03-28 00:56:57 +01:00
2026-02-11 22:42:53 +01:00
auto rhs = m_rhs - > generate_bytecode ( generator , dst ) . value ( ) ;
2021-06-09 06:49:58 +04:30
2025-03-28 00:56:57 +01:00
generator . emit_mov ( dst , rhs ) ;
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { end_block } ) ;
2021-06-09 06:49:58 +04:30
generator . switch_to_basic_block ( end_block ) ;
2024-02-04 08:00:54 +01:00
return dst ;
2021-06-08 02:18:47 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > UnaryExpression : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2021-06-07 19:53:47 +01:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2024-05-09 19:48:25 +02:00
2022-03-27 19:50:09 +01:00
if ( m_op = = UnaryOp : : Delete )
return generator . emit_delete_reference ( m_lhs ) ;
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > src ;
2022-06-11 23:12:22 +01:00
// Typeof needs some special handling for when the LHS is an Identifier. Namely, it shouldn't throw on unresolvable references, but instead return "undefined".
2026-01-06 16:03:23 -08:00
// Skip Not operator as it needs to be evaluated breadth first in order to detect `!!` optimization (otherwise the inner `!x` would eval first).
if ( m_op ! = UnaryOp : : Typeof & & m_op ! = UnaryOp : : Not )
2026-02-11 22:42:53 +01:00
src = m_lhs - > generate_bytecode ( generator ) . value ( ) ;
2024-02-04 08:00:54 +01:00
auto dst = choose_dst ( generator , preferred_dst ) ;
2021-06-07 19:53:47 +01:00
2026-01-06 16:03:23 -08:00
if ( src . has_value ( ) ) {
if ( auto result = try_constant_fold_unary_expression ( generator , * src , m_op ) ; result . has_value ( ) )
2025-03-25 18:01:35 +00:00
return result . release_value ( ) ;
}
2021-06-07 19:53:47 +01:00
switch ( m_op ) {
case UnaryOp : : BitwiseNot :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : BitwiseNot > ( dst , * src ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 19:53:47 +01:00
case UnaryOp : : Not :
2026-01-06 16:03:23 -08:00
if ( auto nested = as_if < UnaryExpression > ( * m_lhs ) ; nested & & nested - > op ( ) = = UnaryOp : : Not ) {
2026-02-11 22:42:53 +01:00
auto value = nested - > lhs ( ) - > generate_bytecode ( generator ) . value ( ) ;
2026-01-06 16:03:23 -08:00
if ( value . operand ( ) . is_constant ( ) )
return generator . add_constant ( Value ( generator . get_constant ( value ) . to_boolean ( ) ) ) ;
generator . emit < Bytecode : : Op : : ToBoolean > ( dst , value ) ;
break ;
}
2026-02-11 22:42:53 +01:00
src = m_lhs - > generate_bytecode ( generator ) . value ( ) ;
2026-01-06 16:03:23 -08:00
if ( auto result = try_constant_fold_unary_expression ( generator , * src , m_op ) ; result . has_value ( ) )
return result . release_value ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Not > ( dst , * src ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 19:53:47 +01:00
case UnaryOp : : Plus :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : UnaryPlus > ( dst , * src ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 19:53:47 +01:00
case UnaryOp : : Minus :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : UnaryMinus > ( dst , * src ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 19:53:47 +01:00
case UnaryOp : : Typeof :
2022-06-11 23:12:22 +01:00
if ( is < Identifier > ( * m_lhs ) ) {
auto & identifier = static_cast < Identifier const & > ( * m_lhs ) ;
2024-02-04 08:00:54 +01:00
if ( ! identifier . is_local ( ) ) {
2024-06-14 09:37:26 +02:00
generator . emit < Bytecode : : Op : : TypeofBinding > ( dst , generator . intern_identifier ( identifier . string ( ) ) ) ;
2024-02-04 08:00:54 +01:00
break ;
2023-07-05 02:17:10 +02:00
}
2022-06-11 23:12:22 +01:00
}
2026-02-11 22:42:53 +01:00
src = m_lhs - > generate_bytecode ( generator ) . value ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Typeof > ( dst , * src ) ;
2021-06-07 20:58:36 -07:00
break ;
2021-06-07 19:53:47 +01:00
case UnaryOp : : Void :
2024-02-04 08:00:54 +01:00
return generator . add_constant ( js_undefined ( ) ) ;
2022-03-27 19:50:09 +01:00
case UnaryOp : : Delete : // Delete is implemented above.
2021-06-07 19:53:47 +01:00
default :
2022-03-27 19:50:09 +01:00
VERIFY_NOT_REACHED ( ) ;
2021-06-07 19:53:47 +01:00
}
2022-02-12 19:54:08 +03:30
2024-02-04 08:00:54 +01:00
return dst ;
2021-06-07 19:53:47 +01:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > NumericLiteral : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-04 11:31:13 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2024-05-14 14:28:22 +02:00
return generator . add_constant ( Value ( m_value ) ) ;
2021-06-04 11:31:13 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > BooleanLiteral : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-07 20:22:15 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2024-02-04 08:00:54 +01:00
return generator . add_constant ( Value ( m_value ) ) ;
2021-06-07 20:22:15 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > NullLiteral : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-07 20:22:15 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2024-02-04 08:00:54 +01:00
return generator . add_constant ( js_null ( ) ) ;
2021-06-07 20:22:15 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > BigIntLiteral : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-08 07:59:25 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2022-10-19 19:17:58 +02:00
// 1. Return the NumericValue of NumericLiteral as defined in 12.8.3.
auto integer = [ & ] {
if ( m_value [ 0 ] = = ' 0 ' & & m_value . length ( ) > = 3 )
if ( m_value [ 1 ] = = ' x ' | | m_value [ 1 ] = = ' X ' )
2024-01-12 21:34:23 +00:00
return MUST ( Crypto : : SignedBigInteger : : from_base ( 16 , m_value . substring ( 2 , m_value . length ( ) - 3 ) ) ) ;
2022-10-19 19:17:58 +02:00
if ( m_value [ 1 ] = = ' o ' | | m_value [ 1 ] = = ' O ' )
2024-01-12 21:34:23 +00:00
return MUST ( Crypto : : SignedBigInteger : : from_base ( 8 , m_value . substring ( 2 , m_value . length ( ) - 3 ) ) ) ;
2022-10-19 19:17:58 +02:00
if ( m_value [ 1 ] = = ' b ' | | m_value [ 1 ] = = ' B ' )
2024-01-12 21:34:23 +00:00
return MUST ( Crypto : : SignedBigInteger : : from_base ( 2 , m_value . substring ( 2 , m_value . length ( ) - 3 ) ) ) ;
return MUST ( Crypto : : SignedBigInteger : : from_base ( 10 , m_value . substring ( 0 , m_value . length ( ) - 1 ) ) ) ;
2022-10-19 19:17:58 +02:00
} ( ) ;
2024-05-14 14:28:22 +02:00
return generator . add_constant ( BigInt : : create ( generator . vm ( ) , move ( integer ) ) ) ;
2021-06-08 07:59:25 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > StringLiteral : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-04 11:31:13 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2024-05-14 14:28:22 +02:00
return generator . add_constant ( PrimitiveString : : create ( generator . vm ( ) , m_value ) ) ;
2021-06-04 11:31:13 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > RegExpLiteral : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2021-06-19 17:17:40 -07:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2025-08-07 19:31:52 -04:00
auto source_index = generator . intern_string ( m_pattern ) ;
auto flags_index = generator . intern_string ( m_flags ) ;
2023-07-13 10:49:07 +02:00
auto regex_index = generator . intern_regex ( Bytecode : : ParsedRegex {
. regex = m_parsed_regex ,
. pattern = m_parsed_pattern ,
. flags = m_parsed_flags ,
} ) ;
2024-02-04 08:00:54 +01:00
auto dst = choose_dst ( generator , preferred_dst ) ;
generator . emit < Bytecode : : Op : : NewRegExp > ( dst , source_index , flags_index , regex_index ) ;
return dst ;
2021-06-19 17:17:40 -07:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > Identifier : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2021-06-04 11:31:13 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2024-02-04 08:00:54 +01:00
if ( is_local ( ) ) {
2026-02-17 17:42:14 +01:00
generator . emit_tdz_check_if_needed ( * this ) ;
return generator . local ( local_index ( ) ) ;
2024-02-04 08:00:54 +01:00
}
2025-11-06 19:00:36 +00:00
if ( is_global ( ) ) {
2026-02-11 22:42:53 +01:00
auto maybe_constant = generator . maybe_generate_builtin_constant ( * this ) ;
2025-11-06 19:00:36 +00:00
if ( maybe_constant . has_value ( ) )
return maybe_constant . release_value ( ) ;
2024-05-09 15:21:24 +02:00
}
2024-02-04 08:00:54 +01:00
auto dst = choose_dst ( generator , preferred_dst ) ;
2023-07-12 04:06:59 +02:00
if ( is_global ( ) ) {
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : GetGlobal > ( dst , generator . intern_identifier ( m_string ) , generator . next_global_variable_cache ( ) ) ;
2023-07-05 02:17:10 +02:00
} else {
2025-05-04 01:41:49 +02:00
if ( declaration_kind ( ) = = DeclarationKind : : Var ) {
generator . emit < Bytecode : : Op : : GetInitializedBinding > ( dst , generator . intern_identifier ( m_string ) ) ;
} else {
generator . emit < Bytecode : : Op : : GetBinding > ( dst , generator . intern_identifier ( m_string ) ) ;
}
2023-07-05 02:17:10 +02:00
}
2024-02-04 08:00:54 +01:00
return dst ;
2021-06-04 11:31:13 +02:00
}
2026-02-11 22:42:53 +01:00
static Optional < ScopedOperand > arguments_to_array_for_call ( Bytecode : : Generator & generator , ReadonlySpan < CallExpression : : Argument > arguments )
2022-09-09 16:05:40 +02:00
{
2024-05-07 21:36:56 +02:00
auto dst = generator . allocate_register ( ) ;
2022-09-09 16:05:40 +02:00
if ( arguments . is_empty ( ) ) {
2025-11-20 22:14:50 +01:00
generator . emit < Bytecode : : Op : : NewArray > ( dst , ReadonlySpan < ScopedOperand > { } ) ;
2024-02-04 08:00:54 +01:00
return dst ;
2022-09-09 16:05:40 +02:00
}
auto first_spread = find_if ( arguments . begin ( ) , arguments . end ( ) , [ ] ( auto el ) { return el . is_spread ; } ) ;
2024-05-08 12:43:08 +02:00
Vector < ScopedOperand > args ;
args . ensure_capacity ( first_spread . index ( ) ) ;
2022-09-09 16:05:40 +02:00
for ( auto it = arguments . begin ( ) ; it ! = first_spread ; + + it ) {
2024-05-08 12:43:08 +02:00
VERIFY ( ! it - > is_spread ) ;
auto reg = generator . allocate_register ( ) ;
2026-02-11 22:42:53 +01:00
auto value = it - > value - > generate_bytecode ( generator ) . value ( ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( reg , value ) ;
2024-05-08 12:43:08 +02:00
args . append ( move ( reg ) ) ;
2022-09-09 16:05:40 +02:00
}
if ( first_spread . index ( ) ! = 0 )
2025-11-20 22:14:50 +01:00
generator . emit_with_extra_operand_slots < Bytecode : : Op : : NewArray > ( args . size ( ) , dst , args . span ( ) ) ;
2022-09-09 16:05:40 +02:00
else
2025-11-20 22:14:50 +01:00
generator . emit < Bytecode : : Op : : NewArray > ( dst , ReadonlySpan < ScopedOperand > { } ) ;
2022-09-09 16:05:40 +02:00
if ( first_spread ! = arguments . end ( ) ) {
for ( auto it = first_spread ; it ! = arguments . end ( ) ; + + it ) {
2026-02-11 22:42:53 +01:00
auto value = it - > value - > generate_bytecode ( generator ) . value ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : ArrayAppend > ( dst , value , it - > is_spread ) ;
2022-09-09 16:05:40 +02:00
}
}
2024-02-04 08:00:54 +01:00
return dst ;
2022-09-09 16:05:40 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > SuperCall : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2022-08-30 18:03:02 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > arguments ;
2022-08-30 18:03:02 +02:00
if ( m_is_synthetic = = IsPartOfSyntheticConstructor : : Yes ) {
// NOTE: This is the case where we have a fake constructor(...args) { super(...args); } which
// shouldn't call @@iterator of %Array.prototype%.
VERIFY ( m_arguments . size ( ) = = 1 ) ;
VERIFY ( m_arguments [ 0 ] . is_spread ) ;
auto const & argument = m_arguments [ 0 ] ;
2024-02-04 08:00:54 +01:00
// This generates a single argument.
2026-02-11 22:42:53 +01:00
arguments = argument . value - > generate_bytecode ( generator ) ;
2022-08-30 18:03:02 +02:00
} else {
2026-02-11 22:42:53 +01:00
arguments = arguments_to_array_for_call ( generator , m_arguments ) . value ( ) ;
2022-08-30 18:03:02 +02:00
}
2024-02-04 08:00:54 +01:00
auto dst = choose_dst ( generator , preferred_dst ) ;
generator . emit < Bytecode : : Op : : SuperCallWithArgumentArray > ( dst , * arguments , m_is_synthetic = = IsPartOfSyntheticConstructor : : Yes ) ;
return dst ;
2022-08-30 18:03:02 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > AssignmentExpression : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2021-06-04 11:31:13 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2021-10-25 15:29:52 +02:00
if ( m_op = = AssignmentOp : : Assignment ) {
2022-07-17 19:23:52 +01:00
// AssignmentExpression : LeftHandSideExpression = AssignmentExpression
return m_lhs . visit (
// 1. If LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral, then
2026-02-11 22:42:53 +01:00
[ & ] ( NonnullRefPtr < Expression const > const & lhs ) - > Optional < ScopedOperand > {
2022-07-17 19:23:52 +01:00
// a. Let lref be the result of evaluating LeftHandSideExpression.
// b. ReturnIfAbrupt(lref).
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > base ;
Optional < ScopedOperand > computed_property ;
Optional < ScopedOperand > this_value ;
2023-07-02 19:26:31 +01:00
bool lhs_is_super_expression = false ;
2022-07-17 19:23:52 +01:00
if ( is < MemberExpression > ( * lhs ) ) {
auto & expression = static_cast < MemberExpression const & > ( * lhs ) ;
2023-07-02 19:26:31 +01:00
lhs_is_super_expression = is < SuperExpression > ( expression . object ( ) ) ;
if ( ! lhs_is_super_expression ) {
2026-02-11 22:42:53 +01:00
auto generated_base = expression . object ( ) . generate_bytecode ( generator ) . value ( ) ;
2025-09-01 15:38:42 +01:00
base = generator . copy_if_needed_to_preserve_evaluation_order ( generated_base ) ;
2023-07-02 19:26:31 +01:00
} else {
// https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
// 1. Let env be GetThisEnvironment().
// 2. Let actualThis be ? env.GetThisBinding().
2024-05-07 12:16:37 +02:00
this_value = generator . get_this ( ) ;
2023-07-02 19:26:31 +01:00
// SuperProperty : super [ Expression ]
// 3. Let propertyNameReference be ? Evaluation of Expression.
// 4. Let propertyNameValue be ? GetValue(propertyNameReference).
}
2022-07-17 19:23:52 +01:00
if ( expression . is_computed ( ) ) {
2026-02-11 22:42:53 +01:00
auto property = expression . property ( ) . generate_bytecode ( generator ) . value ( ) ;
2024-06-01 10:00:32 +02:00
computed_property = generator . copy_if_needed_to_preserve_evaluation_order ( property ) ;
2022-07-17 19:23:52 +01:00
// To be continued later with PutByValue.
} else if ( expression . property ( ) . is_identifier ( ) ) {
// Do nothing, this will be handled by PutById later.
2023-06-23 08:16:17 +02:00
} else if ( expression . property ( ) . is_private_identifier ( ) ) {
// Do nothing, this will be handled by PutPrivateById later.
2022-07-17 19:23:52 +01:00
} else {
2026-02-11 22:05:55 +01:00
VERIFY_NOT_REACHED ( ) ;
2022-07-17 19:23:52 +01:00
}
2023-07-02 19:26:31 +01:00
if ( lhs_is_super_expression ) {
// 5/7. Return ? MakeSuperPropertyReference(actualThis, propertyKey, strict).
// https://tc39.es/ecma262/#sec-makesuperpropertyreference
// 1. Let env be GetThisEnvironment().
// 2. Assert: env.HasSuperBinding() is true.
// 3. Let baseValue be ? env.GetSuperBase().
// 4. Return the Reference Record { [[Base]]: baseValue, [[ReferencedName]]: propertyKey, [[Strict]]: strict, [[ThisValue]]: actualThis }.
2024-05-07 21:36:56 +02:00
base = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : ResolveSuperBase > ( * base ) ;
2023-07-02 19:26:31 +01:00
}
2022-07-17 19:23:52 +01:00
} else if ( is < Identifier > ( * lhs ) ) {
2024-05-14 11:32:04 +02:00
// NOTE: For Identifiers, we cannot perform GetBinding and then write into the reference it retrieves, only SetVariable can do this.
2022-07-17 19:23:52 +01:00
// FIXME: However, this breaks spec as we are doing variable lookup after evaluating the RHS. This is observable in an object environment, where we visibly perform HasOwnProperty and Get(@@unscopables) on the binded object.
} else {
2026-02-11 23:22:50 +01:00
// Per spec 13.15.2 step 1b, we must evaluate the LHS (the call),
// then throw ReferenceError before evaluating the RHS.
2026-02-11 22:42:53 +01:00
( void ) lhs - > generate_bytecode ( generator ) ;
2026-02-11 23:22:50 +01:00
auto exception = generator . allocate_register ( ) ;
generator . emit < Bytecode : : Op : : NewReferenceError > ( exception , generator . intern_string ( ErrorType : : InvalidLeftHandAssignment . message ( ) ) ) ;
generator . perform_needed_unwinds < Bytecode : : Op : : Throw > ( ) ;
generator . emit < Bytecode : : Op : : Throw > ( exception ) ;
2026-03-01 13:17:26 +01:00
return { } ;
2022-07-17 19:23:52 +01:00
}
2026-02-08 12:03:15 +01:00
// c. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then
// i. Let rval be ? NamedEvaluation of AssignmentExpression with argument lref.[[ReferencedName]].
2022-07-17 19:23:52 +01:00
// d. Else,
2026-02-08 12:03:15 +01:00
// i. Let rref be the result of evaluating AssignmentExpression.
// ii. Let rval be ? GetValue(rref).
2026-02-11 22:42:53 +01:00
auto rval = [ & ] ( ) - > ScopedOperand {
2024-02-04 08:00:54 +01:00
if ( lhs - > is_identifier ( ) ) {
2026-02-11 22:42:53 +01:00
return generator . emit_named_evaluation_if_anonymous_function ( * m_rhs , generator . intern_identifier ( static_cast < Identifier const & > ( * lhs ) . string ( ) ) ) ;
2024-02-04 08:00:54 +01:00
} else {
2026-02-11 22:42:53 +01:00
return m_rhs - > generate_bytecode ( generator ) . value ( ) ;
2024-02-04 08:00:54 +01:00
}
2026-02-11 22:42:53 +01:00
} ( ) ;
2022-07-17 19:23:52 +01:00
// e. Perform ? PutValue(lref, rval).
if ( is < Identifier > ( * lhs ) ) {
auto & identifier = static_cast < Identifier const & > ( * lhs ) ;
2026-02-17 17:42:14 +01:00
if ( identifier . is_local ( ) )
generator . emit_tdz_check_if_needed ( identifier ) ;
2024-02-04 08:00:54 +01:00
generator . emit_set_variable ( identifier , rval ) ;
2022-07-17 19:23:52 +01:00
} else if ( is < MemberExpression > ( * lhs ) ) {
auto & expression = static_cast < MemberExpression const & > ( * lhs ) ;
2024-03-29 12:10:52 -04:00
auto base_identifier = generator . intern_identifier_for_expression ( expression . object ( ) ) ;
2022-07-17 19:23:52 +01:00
if ( expression . is_computed ( ) ) {
2023-07-02 19:26:31 +01:00
if ( ! lhs_is_super_expression )
2025-10-10 11:16:02 +02:00
generator . emit_put_by_value ( * base , * computed_property , rval , Bytecode : : PutKind : : Normal , move ( base_identifier ) ) ;
2023-07-02 19:26:31 +01:00
else
2025-10-10 11:16:02 +02:00
generator . emit_put_by_value_with_this ( * base , * computed_property , * this_value , rval , PutKind : : Normal ) ;
2022-07-17 19:23:52 +01:00
} else if ( expression . property ( ) . is_identifier ( ) ) {
2025-12-11 07:57:09 -06:00
auto property_key_table_index = generator . intern_property_key ( as < Identifier > ( expression . property ( ) ) . string ( ) ) ;
2023-07-02 19:26:31 +01:00
if ( ! lhs_is_super_expression )
2025-12-11 07:57:09 -06:00
generator . emit_put_by_id ( * base , property_key_table_index , rval , Bytecode : : PutKind : : Normal , generator . next_property_lookup_cache ( ) , move ( base_identifier ) ) ;
2023-07-02 19:26:31 +01:00
else
2025-12-11 07:57:09 -06:00
generator . emit < Bytecode : : Op : : PutNormalByIdWithThis > ( * base , * this_value , property_key_table_index , rval , generator . next_property_lookup_cache ( ) ) ;
2023-06-23 08:16:17 +02:00
} else if ( expression . property ( ) . is_private_identifier ( ) ) {
2025-01-21 09:12:05 -05:00
auto identifier_table_ref = generator . intern_identifier ( as < PrivateIdentifier > ( expression . property ( ) ) . string ( ) ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : PutPrivateById > ( * base , identifier_table_ref , rval ) ;
2022-07-17 19:23:52 +01:00
} else {
2026-02-11 22:05:55 +01:00
VERIFY_NOT_REACHED ( ) ;
2022-07-17 19:23:52 +01:00
}
} else {
2026-02-12 00:57:59 +01:00
VERIFY_NOT_REACHED ( ) ;
2022-07-17 19:23:52 +01:00
}
// f. Return rval.
2024-02-04 08:00:54 +01:00
return rval ;
2022-07-17 19:23:52 +01:00
} ,
// 2. Let assignmentPattern be the AssignmentPattern that is covered by LeftHandSideExpression.
2026-02-11 22:42:53 +01:00
[ & ] ( NonnullRefPtr < BindingPattern const > const & pattern ) - > Optional < ScopedOperand > {
2022-07-17 19:24:37 +01:00
// 3. Let rref be the result of evaluating AssignmentExpression.
// 4. Let rval be ? GetValue(rref).
2026-02-11 22:42:53 +01:00
auto rval = m_rhs - > generate_bytecode ( generator ) . value ( ) ;
2022-07-17 19:24:37 +01:00
// 5. Perform ? DestructuringAssignmentEvaluation of assignmentPattern with argument rval.
2026-02-11 22:42:53 +01:00
pattern - > generate_bytecode ( generator , Bytecode : : Op : : BindingInitializationMode : : Set , rval ) ;
2022-07-17 19:24:37 +01:00
// 6. Return rval.
2024-02-04 08:00:54 +01:00
return rval ;
2022-07-17 19:23:52 +01:00
} ) ;
2021-10-25 15:29:52 +02:00
}
2021-06-07 20:12:38 +01:00
2023-02-19 22:07:52 +01:00
VERIFY ( m_lhs . has < NonnullRefPtr < Expression const > > ( ) ) ;
2024-02-04 08:00:54 +01:00
auto & lhs_expression = m_lhs . get < NonnullRefPtr < Expression const > > ( ) ;
2022-07-17 19:23:52 +01:00
2026-02-11 22:42:53 +01:00
auto reference_operands = generator . emit_load_from_reference ( lhs_expression ) ;
2026-03-01 13:17:26 +01:00
if ( ! reference_operands . loaded_value . has_value ( ) )
return { } ;
2024-02-04 08:00:54 +01:00
auto lhs = reference_operands . loaded_value . value ( ) ;
2021-06-07 20:12:38 +01:00
2021-10-25 15:29:52 +02:00
Bytecode : : BasicBlock * rhs_block_ptr { nullptr } ;
2024-02-04 08:00:54 +01:00
Bytecode : : BasicBlock * lhs_block_ptr { nullptr } ;
2021-10-25 15:29:52 +02:00
Bytecode : : BasicBlock * end_block_ptr { nullptr } ;
2021-06-07 20:12:38 +01:00
2021-10-25 15:29:52 +02:00
// Logical assignments short circuit.
if ( m_op = = AssignmentOp : : AndAssignment ) { // &&=
rhs_block_ptr = & generator . make_block ( ) ;
2024-02-04 08:00:54 +01:00
lhs_block_ptr = & generator . make_block ( ) ;
2021-10-25 15:29:52 +02:00
end_block_ptr = & generator . make_block ( ) ;
2021-06-09 21:14:31 +01:00
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
lhs ,
2021-10-25 15:29:52 +02:00
Bytecode : : Label { * rhs_block_ptr } ,
2024-02-04 08:00:54 +01:00
Bytecode : : Label { * lhs_block_ptr } ) ;
2021-10-25 15:29:52 +02:00
} else if ( m_op = = AssignmentOp : : OrAssignment ) { // ||=
rhs_block_ptr = & generator . make_block ( ) ;
2024-02-04 08:00:54 +01:00
lhs_block_ptr = & generator . make_block ( ) ;
2021-10-25 15:29:52 +02:00
end_block_ptr = & generator . make_block ( ) ;
2021-06-09 21:14:31 +01:00
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
lhs ,
Bytecode : : Label { * lhs_block_ptr } ,
2021-10-25 15:29:52 +02:00
Bytecode : : Label { * rhs_block_ptr } ) ;
} else if ( m_op = = AssignmentOp : : NullishAssignment ) { // ??=
rhs_block_ptr = & generator . make_block ( ) ;
2024-02-04 08:00:54 +01:00
lhs_block_ptr = & generator . make_block ( ) ;
2021-10-25 15:29:52 +02:00
end_block_ptr = & generator . make_block ( ) ;
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : JumpNullish > (
2024-02-04 08:00:54 +01:00
lhs ,
2021-10-25 15:29:52 +02:00
Bytecode : : Label { * rhs_block_ptr } ,
2024-02-04 08:00:54 +01:00
Bytecode : : Label { * lhs_block_ptr } ) ;
2021-06-04 11:31:13 +02:00
}
2021-10-25 15:29:52 +02:00
if ( rhs_block_ptr )
generator . switch_to_basic_block ( * rhs_block_ptr ) ;
2026-02-11 22:42:53 +01:00
auto rhs = [ & ] ( ) - > ScopedOperand {
2024-02-04 08:00:54 +01:00
if ( lhs_expression - > is_identifier ( ) ) {
2026-02-11 22:42:53 +01:00
return generator . emit_named_evaluation_if_anonymous_function ( * m_rhs , generator . intern_identifier ( static_cast < Identifier const & > ( * lhs_expression ) . string ( ) ) ) ;
2024-02-04 08:00:54 +01:00
}
2026-02-11 22:42:53 +01:00
return m_rhs - > generate_bytecode ( generator ) . value ( ) ;
} ( ) ;
2023-06-26 15:26:46 +02:00
2024-05-14 13:15:14 +02:00
// OPTIMIZATION: If LHS is a local, we can write the result directly into it.
auto dst = [ & ] {
if ( lhs . operand ( ) . is_local ( ) )
return lhs ;
return choose_dst ( generator , preferred_dst ) ;
} ( ) ;
2021-10-25 15:29:52 +02:00
switch ( m_op ) {
case AssignmentOp : : AdditionAssignment :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Add > ( dst , lhs , rhs ) ;
2021-10-25 15:29:52 +02:00
break ;
case AssignmentOp : : SubtractionAssignment :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Sub > ( dst , lhs , rhs ) ;
2021-10-25 15:29:52 +02:00
break ;
case AssignmentOp : : MultiplicationAssignment :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Mul > ( dst , lhs , rhs ) ;
2021-10-25 15:29:52 +02:00
break ;
case AssignmentOp : : DivisionAssignment :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Div > ( dst , lhs , rhs ) ;
2021-10-25 15:29:52 +02:00
break ;
case AssignmentOp : : ModuloAssignment :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Mod > ( dst , lhs , rhs ) ;
2021-10-25 15:29:52 +02:00
break ;
case AssignmentOp : : ExponentiationAssignment :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Exp > ( dst , lhs , rhs ) ;
2021-10-25 15:29:52 +02:00
break ;
case AssignmentOp : : BitwiseAndAssignment :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : BitwiseAnd > ( dst , lhs , rhs ) ;
2021-10-25 15:29:52 +02:00
break ;
case AssignmentOp : : BitwiseOrAssignment :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : BitwiseOr > ( dst , lhs , rhs ) ;
2021-10-25 15:29:52 +02:00
break ;
case AssignmentOp : : BitwiseXorAssignment :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : BitwiseXor > ( dst , lhs , rhs ) ;
2021-10-25 15:29:52 +02:00
break ;
case AssignmentOp : : LeftShiftAssignment :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : LeftShift > ( dst , lhs , rhs ) ;
2021-10-25 15:29:52 +02:00
break ;
case AssignmentOp : : RightShiftAssignment :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : RightShift > ( dst , lhs , rhs ) ;
2021-10-25 15:29:52 +02:00
break ;
case AssignmentOp : : UnsignedRightShiftAssignment :
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : UnsignedRightShift > ( dst , lhs , rhs ) ;
2021-10-25 15:29:52 +02:00
break ;
case AssignmentOp : : AndAssignment :
case AssignmentOp : : OrAssignment :
case AssignmentOp : : NullishAssignment :
2025-03-28 00:56:57 +01:00
generator . emit_mov ( dst , rhs ) ;
2024-02-04 08:00:54 +01:00
break ;
2021-10-25 15:29:52 +02:00
default :
2026-02-11 22:05:55 +01:00
VERIFY_NOT_REACHED ( ) ;
2021-06-04 20:47:07 +02:00
}
2024-02-04 08:00:54 +01:00
if ( lhs_expression - > is_identifier ( ) )
generator . emit_set_variable ( static_cast < Identifier const & > ( * lhs_expression ) , dst ) ;
2023-10-04 15:22:07 +02:00
else
2026-02-11 22:42:53 +01:00
generator . emit_store_to_reference ( reference_operands , dst ) ;
2021-10-25 15:29:52 +02:00
2024-02-04 08:00:54 +01:00
if ( rhs_block_ptr ) {
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { * end_block_ptr } ) ;
}
if ( lhs_block_ptr ) {
generator . switch_to_basic_block ( * lhs_block_ptr ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( dst , lhs ) ;
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { * end_block_ptr } ) ;
2024-02-04 08:00:54 +01:00
}
if ( end_block_ptr ) {
2021-10-25 15:29:52 +02:00
generator . switch_to_basic_block ( * end_block_ptr ) ;
}
2022-02-12 19:54:08 +03:30
2024-02-04 08:00:54 +01:00
return dst ;
2021-06-04 11:31:13 +02:00
}
2022-06-11 23:09:37 +01:00
// 14.13.3 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-labelled-statements-runtime-semantics-evaluation
// LabelledStatement : LabelIdentifier : LabelledItem
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > LabelledStatement : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2022-06-11 23:09:37 +01:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2022-06-11 23:09:37 +01:00
// Return ? LabelledEvaluation of this LabelledStatement with argument « ».
return generate_labelled_evaluation ( generator , { } ) ;
}
// 14.13.4 Runtime Semantics: LabelledEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-labelledevaluation
// LabelledStatement : LabelIdentifier : LabelledItem
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > LabelledStatement : : generate_labelled_evaluation ( Bytecode : : Generator & generator , Vector < FlyString > const & label_set , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2022-06-11 23:09:37 +01:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2022-06-11 23:09:37 +01:00
// Convert the m_labelled_item NNRP to a reference early so we don't have to do it every single time we want to use it.
auto const & labelled_item = * m_labelled_item ;
// 1. Let label be the StringValue of LabelIdentifier.
// NOTE: Not necessary, this is m_label.
// 2. Let newLabelSet be the list-concatenation of labelSet and « label ».
// FIXME: Avoid copy here.
auto new_label_set = label_set ;
new_label_set . append ( m_label ) ;
// 3. Let stmtResult be LabelledEvaluation of LabelledItem with argument newLabelSet.
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > stmt_result ;
2022-06-11 23:09:37 +01:00
if ( is < IterationStatement > ( labelled_item ) ) {
auto const & iteration_statement = static_cast < IterationStatement const & > ( labelled_item ) ;
2026-02-11 22:42:53 +01:00
stmt_result = iteration_statement . generate_labelled_evaluation ( generator , new_label_set ) ;
2022-06-11 23:09:37 +01:00
} else if ( is < SwitchStatement > ( labelled_item ) ) {
auto const & switch_statement = static_cast < SwitchStatement const & > ( labelled_item ) ;
2026-02-11 22:42:53 +01:00
stmt_result = switch_statement . generate_labelled_evaluation ( generator , new_label_set ) ;
2022-06-11 23:09:37 +01:00
} else if ( is < LabelledStatement > ( labelled_item ) ) {
auto const & labelled_statement = static_cast < LabelledStatement const & > ( labelled_item ) ;
2026-02-11 22:42:53 +01:00
stmt_result = labelled_statement . generate_labelled_evaluation ( generator , new_label_set ) ;
2022-06-11 23:09:37 +01:00
} else {
auto & labelled_break_block = generator . make_block ( ) ;
// NOTE: We do not need a continuable scope as `continue;` is not allowed outside of iteration statements, throwing a SyntaxError in the parser.
generator . begin_breakable_scope ( Bytecode : : Label { labelled_break_block } , new_label_set ) ;
2026-02-11 22:42:53 +01:00
stmt_result = labelled_item . generate_bytecode ( generator ) ;
2022-06-11 23:09:37 +01:00
generator . end_breakable_scope ( ) ;
if ( ! generator . is_current_block_terminated ( ) ) {
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { labelled_break_block } ) ;
2022-06-11 23:09:37 +01:00
}
generator . switch_to_basic_block ( labelled_break_block ) ;
}
// 4. If stmtResult.[[Type]] is break and SameValue(stmtResult.[[Target]], label) is true, then
// a. Set stmtResult to NormalCompletion(stmtResult.[[Value]]).
// NOTE: These steps are performed by making labelled break jump straight to the appropriate break block, which preserves the statement result's value in the accumulator.
// 5. Return Completion(stmtResult).
2024-02-04 08:00:54 +01:00
return stmt_result ;
2022-06-11 23:09:37 +01:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > WhileStatement : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2022-06-11 23:09:37 +01:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2022-06-11 23:09:37 +01:00
return generate_labelled_evaluation ( generator , { } ) ;
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > WhileStatement : : generate_labelled_evaluation ( Bytecode : : Generator & generator , Vector < FlyString > const & label_set , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-04 12:07:38 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2026-01-25 10:57:43 -08:00
2021-06-09 06:49:58 +04:30
auto & test_block = generator . make_block ( ) ;
2024-05-14 10:57:06 +02:00
Optional < ScopedOperand > completion ;
if ( generator . must_propagate_completion ( ) ) {
completion = generator . allocate_register ( ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( * completion , generator . add_constant ( js_undefined ( ) ) ) ;
2024-05-14 10:57:06 +02:00
}
2021-06-09 06:49:58 +04:30
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { test_block } ) ;
2021-06-09 06:49:58 +04:30
generator . switch_to_basic_block ( test_block ) ;
2026-02-11 22:42:53 +01:00
auto test = m_test - > generate_bytecode ( generator ) . value ( ) ;
2026-01-25 10:57:43 -08:00
// OPTIMIZATION: If predicate is always false, ignore body and exit early
if ( auto constant = generator . try_get_constant ( test ) ; constant . has_value ( ) & & ! constant - > to_boolean_slow_case ( ) ) {
return completion ;
}
// test
// jump if_false (true) end (false) body
// body
// jump always (true) test
// end
auto & body_block = generator . make_block ( ) ;
auto & end_block = generator . make_block ( ) ;
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
test ,
2021-06-09 06:49:58 +04:30
Bytecode : : Label { body_block } ,
2024-02-04 08:00:54 +01:00
Bytecode : : Label { end_block } ) ;
2021-06-09 06:49:58 +04:30
generator . switch_to_basic_block ( body_block ) ;
2026-02-10 23:25:24 +01:00
generator . begin_continuable_scope ( Bytecode : : Label { test_block } , label_set , completion ) ;
generator . begin_breakable_scope ( Bytecode : : Label { end_block } , label_set , completion ) ;
{
Optional < Bytecode : : Generator : : CompletionRegisterScope > completion_scope ;
if ( completion . has_value ( ) )
completion_scope . emplace ( generator , * completion ) ;
2026-02-11 22:42:53 +01:00
auto body = m_body - > generate_bytecode ( generator ) ;
2026-02-10 23:25:24 +01:00
if ( ! generator . is_current_block_terminated ( ) & & completion . has_value ( ) & & body . has_value ( ) )
generator . emit_mov ( * completion , * body ) ;
}
2022-03-14 02:13:33 +00:00
generator . end_breakable_scope ( ) ;
generator . end_continuable_scope ( ) ;
2026-02-10 23:25:24 +01:00
if ( ! generator . is_current_block_terminated ( ) )
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { test_block } ) ;
2022-02-12 19:54:08 +03:30
2023-06-25 20:52:35 +01:00
generator . switch_to_basic_block ( end_block ) ;
2024-05-14 10:57:06 +02:00
return completion ;
2021-06-04 12:07:38 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > DoWhileStatement : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2022-06-11 23:09:37 +01:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2022-06-11 23:09:37 +01:00
return generate_labelled_evaluation ( generator , { } ) ;
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > DoWhileStatement : : generate_labelled_evaluation ( Bytecode : : Generator & generator , Vector < FlyString > const & label_set , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-04 12:20:44 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2021-06-09 06:49:58 +04:30
// jump always (true) body
// test
// jump if_false (true) end (false) body
// body
// jump always (true) test
// end
auto & body_block = generator . make_block ( ) ;
2024-05-06 13:38:08 +02:00
auto & test_block = generator . make_block ( ) ;
2023-06-25 20:52:35 +01:00
auto & load_result_and_jump_to_end_block = generator . make_block ( ) ;
2021-06-09 06:49:58 +04:30
auto & end_block = generator . make_block ( ) ;
2024-05-14 10:57:06 +02:00
Optional < ScopedOperand > completion ;
if ( generator . must_propagate_completion ( ) ) {
completion = generator . allocate_register ( ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( * completion , generator . add_constant ( js_undefined ( ) ) ) ;
2024-05-14 10:57:06 +02:00
}
2021-06-09 06:49:58 +04:30
// jump to the body block
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { body_block } ) ;
2021-06-09 06:49:58 +04:30
generator . switch_to_basic_block ( test_block ) ;
2026-02-11 22:42:53 +01:00
auto test = m_test - > generate_bytecode ( generator ) . value ( ) ;
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
test ,
2021-06-09 06:49:58 +04:30
Bytecode : : Label { body_block } ,
2023-06-25 20:52:35 +01:00
Bytecode : : Label { load_result_and_jump_to_end_block } ) ;
2021-06-09 06:49:58 +04:30
generator . switch_to_basic_block ( body_block ) ;
2026-02-10 23:25:24 +01:00
generator . begin_continuable_scope ( Bytecode : : Label { test_block } , label_set , completion ) ;
generator . begin_breakable_scope ( Bytecode : : Label { end_block } , label_set , completion ) ;
{
Optional < Bytecode : : Generator : : CompletionRegisterScope > completion_scope ;
if ( completion . has_value ( ) )
completion_scope . emplace ( generator , * completion ) ;
2026-02-11 22:42:53 +01:00
auto body = m_body - > generate_bytecode ( generator ) ;
2026-02-10 23:25:24 +01:00
if ( ! generator . is_current_block_terminated ( ) & & completion . has_value ( ) & & body . has_value ( ) )
generator . emit_mov ( * completion , * body ) ;
}
2022-03-14 02:13:33 +00:00
generator . end_breakable_scope ( ) ;
generator . end_continuable_scope ( ) ;
2026-02-10 23:25:24 +01:00
if ( ! generator . is_current_block_terminated ( ) )
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { test_block } ) ;
2022-02-12 19:54:08 +03:30
2023-06-25 20:52:35 +01:00
generator . switch_to_basic_block ( load_result_and_jump_to_end_block ) ;
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { end_block } ) ;
generator . switch_to_basic_block ( end_block ) ;
2024-05-14 10:57:06 +02:00
return completion ;
2021-06-04 12:20:44 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ForStatement : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2022-06-11 23:09:37 +01:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2022-06-11 23:09:37 +01:00
return generate_labelled_evaluation ( generator , { } ) ;
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ForStatement : : generate_labelled_evaluation ( Bytecode : : Generator & generator , Vector < FlyString > const & label_set , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-08 10:54:40 +01:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2021-06-09 06:49:58 +04:30
// init
// jump always (true) test
// test
// jump if_true (true) body (false) end
// body
// jump always (true) update
// update
// jump always (true) test
// end
// If 'test' is missing, fuse the 'test' and 'body' basic blocks
// If 'update' is missing, fuse the 'body' and 'update' basic blocks
Bytecode : : BasicBlock * test_block_ptr { nullptr } ;
Bytecode : : BasicBlock * body_block_ptr { nullptr } ;
Bytecode : : BasicBlock * update_block_ptr { nullptr } ;
2022-03-14 02:39:24 +00:00
bool has_lexical_environment = false ;
2024-07-16 16:56:02 -04:00
Vector < IdentifierTableIndex > per_iteration_bindings ;
2022-03-14 02:39:24 +00:00
if ( m_init ) {
if ( m_init - > is_variable_declaration ( ) ) {
2025-01-21 09:12:05 -05:00
auto & variable_declaration = as < VariableDeclaration > ( * m_init ) ;
2022-03-14 02:39:24 +00:00
2023-07-13 21:30:36 +02:00
auto has_non_local_variables = false ;
MUST ( variable_declaration . for_each_bound_identifier ( [ & ] ( auto const & identifier ) {
if ( ! identifier . is_local ( ) )
has_non_local_variables = true ;
} ) ) ;
if ( variable_declaration . is_lexical_declaration ( ) & & has_non_local_variables ) {
2022-03-14 02:39:24 +00:00
has_lexical_environment = true ;
2024-07-16 16:56:02 -04:00
// Setup variable scope for bound identifiers
2023-06-16 16:34:47 +02:00
generator . begin_variable_scope ( ) ;
2022-03-14 02:39:24 +00:00
bool is_const = variable_declaration . is_constant_declaration ( ) ;
2023-02-27 22:13:37 +00:00
// NOTE: Nothing in the callback throws an exception.
2023-07-13 21:30:36 +02:00
MUST ( variable_declaration . for_each_bound_identifier ( [ & ] ( auto const & identifier ) {
if ( identifier . is_local ( ) )
return ;
auto index = generator . intern_identifier ( identifier . string ( ) ) ;
2025-11-20 22:14:50 +01:00
generator . emit < Bytecode : : Op : : CreateVariable > ( index , Bytecode : : Op : : EnvironmentMode : : Lexical , is_const , false , false ) ;
2024-07-16 16:56:02 -04:00
if ( ! is_const ) {
per_iteration_bindings . append ( index ) ;
}
2023-02-27 22:13:37 +00:00
} ) ) ;
2022-03-14 02:39:24 +00:00
}
}
2026-02-11 22:42:53 +01:00
( void ) m_init - > generate_bytecode ( generator ) ;
2022-03-14 02:39:24 +00:00
}
2021-06-07 20:58:36 -07:00
2024-07-16 16:56:02 -04:00
// CreatePerIterationEnvironment (https://tc39.es/ecma262/multipage/ecmascript-language-statements-and-declarations.html#sec-createperiterationenvironment)
auto generate_per_iteration_bindings = [ & per_iteration_bindings = static_cast < Vector < IdentifierTableIndex > const & > ( per_iteration_bindings ) ,
& generator ] ( ) {
if ( per_iteration_bindings . is_empty ( ) ) {
return ;
}
// Copy all the last values into registers for use in step 1.e.iii
// Register copies of bindings are required since the changing of the
// running execution context in the final step requires leaving the
// current variable scope before creating "thisIterationEnv"
Vector < ScopedOperand > registers ;
for ( auto const & binding : per_iteration_bindings ) {
auto reg = generator . allocate_register ( ) ;
generator . emit < Bytecode : : Op : : GetBinding > ( reg , binding ) ;
registers . append ( reg ) ;
}
generator . end_variable_scope ( ) ;
generator . begin_variable_scope ( ) ;
for ( size_t i = 0 ; i < per_iteration_bindings . size ( ) ; + + i ) {
2025-11-20 22:14:50 +01:00
generator . emit < Bytecode : : Op : : CreateVariable > ( per_iteration_bindings [ i ] , Bytecode : : Op : : EnvironmentMode : : Lexical , false , false , false ) ;
2024-07-16 16:56:02 -04:00
generator . emit < Bytecode : : Op : : InitializeLexicalBinding > ( per_iteration_bindings [ i ] , registers [ i ] ) ;
}
} ;
2024-07-24 10:35:39 +02:00
if ( m_init ) {
// CreatePerIterationEnvironment where lastIterationEnv is the variable
// scope created above for bound identifiers
generate_per_iteration_bindings ( ) ;
}
2021-06-09 06:49:58 +04:30
body_block_ptr = & generator . make_block ( ) ;
if ( m_update )
update_block_ptr = & generator . make_block ( ) ;
else
update_block_ptr = body_block_ptr ;
2024-05-06 09:34:42 +02:00
if ( m_test )
test_block_ptr = & generator . make_block ( ) ;
else
test_block_ptr = body_block_ptr ;
2024-05-06 10:10:38 +02:00
auto & end_block = generator . make_block ( ) ;
2026-02-10 23:25:24 +01:00
Optional < ScopedOperand > completion ;
if ( generator . must_propagate_completion ( ) ) {
completion = generator . allocate_register ( ) ;
generator . emit_mov ( * completion , generator . add_constant ( js_undefined ( ) ) ) ;
}
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { * test_block_ptr } ) ;
2021-06-09 06:49:58 +04:30
2021-06-08 10:54:40 +01:00
if ( m_test ) {
2021-06-09 06:49:58 +04:30
generator . switch_to_basic_block ( * test_block_ptr ) ;
2023-06-25 20:52:35 +01:00
2026-02-11 22:42:53 +01:00
auto test = m_test - > generate_bytecode ( generator ) . value ( ) ;
2026-01-25 10:57:43 -08:00
// OPTIMIZATION: test value is always falsey, skip body entirely
if ( auto constant = generator . try_get_constant ( test ) ; constant . has_value ( ) & & ! constant - > to_boolean_slow_case ( ) ) {
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { end_block } ) ;
generator . switch_to_basic_block ( end_block ) ;
2026-02-10 23:25:24 +01:00
if ( has_lexical_environment )
generator . end_variable_scope ( ) ;
return completion ;
2026-01-25 10:57:43 -08:00
}
2024-05-09 15:13:31 +02:00
generator . emit_jump_if ( test , Bytecode : : Label { * body_block_ptr } , Bytecode : : Label { end_block } ) ;
2021-06-08 10:54:40 +01:00
}
2021-06-07 20:58:36 -07:00
2023-06-15 19:13:00 +02:00
if ( m_update ) {
generator . switch_to_basic_block ( * update_block_ptr ) ;
2023-06-25 20:52:35 +01:00
2026-02-11 22:42:53 +01:00
( void ) m_update - > generate_bytecode ( generator ) ;
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { * test_block_ptr } ) ;
2023-06-15 19:13:00 +02:00
}
2021-06-09 06:49:58 +04:30
generator . switch_to_basic_block ( * body_block_ptr ) ;
2026-02-10 23:25:24 +01:00
generator . begin_continuable_scope ( Bytecode : : Label { m_update ? * update_block_ptr : * test_block_ptr } , label_set , completion ) ;
generator . begin_breakable_scope ( Bytecode : : Label { end_block } , label_set , completion ) ;
{
Optional < Bytecode : : Generator : : CompletionRegisterScope > completion_scope ;
if ( completion . has_value ( ) )
completion_scope . emplace ( generator , * completion ) ;
2026-02-11 22:42:53 +01:00
auto body = m_body - > generate_bytecode ( generator ) ;
2026-02-10 23:25:24 +01:00
if ( ! generator . is_current_block_terminated ( ) & & completion . has_value ( ) & & body . has_value ( ) )
generator . emit_mov ( * completion , * body ) ;
}
2022-07-17 19:22:18 +01:00
generator . end_breakable_scope ( ) ;
2021-06-08 10:54:40 +01:00
generator . end_continuable_scope ( ) ;
2021-06-09 06:49:58 +04:30
2021-06-09 19:57:18 +02:00
if ( ! generator . is_current_block_terminated ( ) ) {
2024-07-24 10:35:39 +02:00
// CreatePerIterationEnvironment where lastIterationEnv is the environment
// created by the previous CreatePerIterationEnvironment setup
generate_per_iteration_bindings ( ) ;
2021-06-09 19:57:18 +02:00
if ( m_update ) {
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { * update_block_ptr } ) ;
2023-06-15 19:13:00 +02:00
} else {
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { * test_block_ptr } ) ;
2021-06-09 19:57:18 +02:00
}
2021-06-09 06:49:58 +04:30
}
2022-02-12 19:54:08 +03:30
2022-06-30 15:55:57 +01:00
generator . switch_to_basic_block ( end_block ) ;
2024-07-16 16:56:02 -04:00
// Leave the environment setup by CreatePerIterationEnvironment or if there
// are no perIterationBindings the variable scope created for bound
// identifiers
2022-03-15 01:44:32 +00:00
if ( has_lexical_environment )
generator . end_variable_scope ( ) ;
2026-02-10 23:25:24 +01:00
return completion ;
2021-06-08 10:54:40 +01:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ObjectExpression : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-04 20:30:23 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2021-06-04 20:30:23 +02:00
2025-03-27 15:49:49 +00:00
auto object = choose_dst ( generator , preferred_dst ) ;
2024-02-04 08:00:54 +01:00
2026-01-20 15:11:43 +01:00
// Determine if this is a simple object literal (all KeyValue with StringLiteral keys
// that are not numeric indices). Simple literals can benefit from shape caching with
// direct property offset writes. Numeric string keys like "0" are stored in indexed
// storage rather than shape-based storage, so they can't use the fast path.
2026-01-09 18:55:00 +01:00
bool is_simple = ! m_properties . is_empty ( ) ;
for ( auto & property : m_properties ) {
if ( property - > type ( ) ! = ObjectProperty : : Type : : KeyValue | | ! is < StringLiteral > ( property - > key ( ) ) ) {
is_simple = false ;
break ;
}
2026-01-20 15:11:43 +01:00
// Check if the key is a numeric index (would be stored in indexed storage)
auto const & key = static_cast < StringLiteral const & > ( property - > key ( ) ) . value ( ) ;
if ( ! key . is_empty ( ) & & ! ( key . code_unit_at ( 0 ) = = ' 0 ' & & key . length_in_code_units ( ) > 1 ) ) {
auto property_index = key . to_number < u32 > ( TrimWhitespace : : No ) ;
if ( property_index . has_value ( ) & & property_index . value ( ) < NumericLimits < u32 > : : max ( ) ) {
is_simple = false ;
break ;
}
}
2026-01-09 18:55:00 +01:00
}
Optional < u32 > shape_cache_index ;
if ( is_simple )
shape_cache_index = generator . next_object_shape_cache ( ) ;
generator . emit < Bytecode : : Op : : NewObject > ( object , shape_cache_index . value_or ( NumericLimits < u32 > : : max ( ) ) ) ;
2024-02-04 08:00:54 +01:00
if ( m_properties . is_empty ( ) )
return object ;
2021-06-11 19:40:48 +03:00
2024-02-04 08:00:54 +01:00
generator . push_home_object ( object ) ;
2023-06-16 10:25:05 +02:00
2026-01-09 18:55:00 +01:00
u32 property_slot = 0 ;
2021-06-11 19:40:48 +03:00
for ( auto & property : m_properties ) {
2025-10-10 11:16:02 +02:00
Bytecode : : PutKind property_kind ;
2023-03-06 14:17:01 +01:00
switch ( property - > type ( ) ) {
2022-03-31 00:59:58 +04:30
case ObjectProperty : : Type : : KeyValue :
2025-10-10 11:16:02 +02:00
property_kind = Bytecode : : PutKind : : Own ;
2022-03-31 00:59:58 +04:30
break ;
case ObjectProperty : : Type : : Getter :
2025-10-10 11:16:02 +02:00
property_kind = Bytecode : : PutKind : : Getter ;
2022-03-31 00:59:58 +04:30
break ;
case ObjectProperty : : Type : : Setter :
2025-10-10 11:16:02 +02:00
property_kind = Bytecode : : PutKind : : Setter ;
2022-03-31 00:59:58 +04:30
break ;
case ObjectProperty : : Type : : ProtoSetter :
2025-10-10 11:16:02 +02:00
property_kind = Bytecode : : PutKind : : Prototype ;
2022-03-31 00:59:58 +04:30
break ;
2024-11-01 22:00:32 +01:00
case ObjectProperty : : Type : : Spread :
2026-02-11 22:42:53 +01:00
generator . emit < Bytecode : : Op : : PutBySpread > ( object , property - > key ( ) . generate_bytecode ( generator ) . value ( ) ) ;
2024-11-01 22:00:32 +01:00
continue ;
2022-03-31 00:59:58 +04:30
}
2021-06-11 19:40:48 +03:00
2023-03-06 14:17:01 +01:00
if ( is < StringLiteral > ( property - > key ( ) ) ) {
auto & string_literal = static_cast < StringLiteral const & > ( property - > key ( ) ) ;
2021-06-11 19:40:48 +03:00
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > value ;
2025-10-10 11:16:02 +02:00
if ( property_kind = = Bytecode : : PutKind : : Prototype ) {
2026-02-11 22:42:53 +01:00
value = property - > value ( ) . generate_bytecode ( generator ) . value ( ) ;
2024-11-01 22:00:32 +01:00
} else {
2025-03-18 18:08:02 -05:00
auto identifier = string_literal . value ( ) ;
2025-10-10 11:16:02 +02:00
if ( property_kind = = Bytecode : : PutKind : : Getter )
2025-08-06 11:12:58 -04:00
identifier = Utf16String : : formatted ( " get {} " , identifier ) ;
2025-10-10 11:16:02 +02:00
else if ( property_kind = = Bytecode : : PutKind : : Setter )
2025-08-06 11:12:58 -04:00
identifier = Utf16String : : formatted ( " set {} " , identifier ) ;
2023-11-05 11:21:32 +01:00
auto name = generator . intern_identifier ( identifier ) ;
2026-02-11 22:42:53 +01:00
value = generator . emit_named_evaluation_if_anonymous_function ( property - > value ( ) , name , { } , property - > is_method ( ) ) ;
2023-06-25 19:25:38 +02:00
}
2022-03-31 00:59:58 +04:30
2025-12-11 07:57:09 -06:00
auto property_key_table_index = generator . intern_property_key ( string_literal . value ( ) ) ;
2026-01-09 18:55:00 +01:00
// For simple object literals, use InitObjectLiteralProperty for direct offset writes
if ( is_simple ) {
generator . emit < Bytecode : : Op : : InitObjectLiteralProperty > ( object , property_key_table_index , * value , * shape_cache_index , property_slot + + ) ;
} else {
generator . emit_put_by_id ( object , property_key_table_index , * value , property_kind , generator . next_property_lookup_cache ( ) ) ;
}
2021-06-11 19:40:48 +03:00
} else {
2026-02-11 22:42:53 +01:00
auto property_name = property - > key ( ) . generate_bytecode ( generator ) . value ( ) ;
2026-02-08 12:32:38 +01:00
// ComputedPropertyName evaluation calls ToPropertyKey, which includes ToPrimitive(hint: string).
// This must happen before the value expression is evaluated per the spec for
// PropertyDefinitionEvaluation (PropertyDefinition : PropertyName : AssignmentExpression):
// 1. Let propKey be ? Evaluation of PropertyName.
// [then] 5/6. Evaluate the AssignmentExpression.
// ToPrimitive is the only step in ToPropertyKey with user-observable side effects.
// After this, the ToPrimitive inside put_by_value's to_property_key is a no-op.
generator . emit < Bytecode : : Op : : ToPrimitiveWithStringHint > ( property_name , property_name ) ;
2026-02-11 22:42:53 +01:00
auto value = generator . emit_named_evaluation_if_anonymous_function ( property - > value ( ) , { } , { } , property - > is_method ( ) ) ;
2022-03-31 00:59:58 +04:30
2025-04-03 14:29:44 +02:00
generator . emit_put_by_value ( object , property_name , value , property_kind , { } ) ;
2021-06-11 19:40:48 +03:00
}
}
2023-06-16 10:25:05 +02:00
generator . pop_home_object ( ) ;
2026-01-09 18:55:00 +01:00
if ( shape_cache_index . has_value ( ) )
generator . emit < Bytecode : : Op : : CacheObjectShape > ( object , * shape_cache_index ) ;
2024-02-04 08:00:54 +01:00
return object ;
2021-06-04 20:30:23 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ArrayExpression : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2021-06-08 23:06:52 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2022-09-09 15:23:02 +02:00
if ( m_elements . is_empty ( ) ) {
2024-02-04 08:00:54 +01:00
auto dst = choose_dst ( generator , preferred_dst ) ;
2025-11-20 22:14:50 +01:00
generator . emit < Bytecode : : Op : : NewArray > ( dst , ReadonlySpan < ScopedOperand > { } ) ;
2024-02-04 08:00:54 +01:00
return dst ;
2022-03-14 14:48:42 +00:00
}
2021-06-08 23:06:52 +02:00
2023-11-17 22:07:23 +02:00
if ( all_of ( m_elements , [ ] ( auto element ) { return ! element | | is < PrimitiveLiteral > ( * element ) ; } ) ) {
// If all elements are constant primitives, we can just emit a single instruction to initialize the array,
// instead of emitting instructions to manually evaluate them one-by-one
2024-03-03 12:37:28 +01:00
Vector < Value > values ;
2025-04-04 23:16:34 +02:00
values . resize_with_default_value ( m_elements . size ( ) , js_special_empty_value ( ) ) ;
2023-11-17 22:07:23 +02:00
for ( auto i = 0u ; i < m_elements . size ( ) ; + + i ) {
if ( ! m_elements [ i ] )
continue ;
values [ i ] = static_cast < PrimitiveLiteral const & > ( * m_elements [ i ] ) . value ( ) ;
}
2024-02-04 08:00:54 +01:00
auto dst = choose_dst ( generator , preferred_dst ) ;
2024-03-03 12:37:28 +01:00
generator . emit_with_extra_value_slots < Bytecode : : Op : : NewPrimitiveArray > ( values . size ( ) , dst , values ) ;
2024-02-04 08:00:54 +01:00
return dst ;
2023-11-17 22:07:23 +02:00
}
2022-09-09 15:23:02 +02:00
auto first_spread = find_if ( m_elements . begin ( ) , m_elements . end ( ) , [ ] ( auto el ) { return el & & is < SpreadExpression > ( * el ) ; } ) ;
2024-05-08 12:43:08 +02:00
Vector < ScopedOperand > args ;
args . ensure_capacity ( m_elements . size ( ) ) ;
2022-09-09 15:23:02 +02:00
for ( auto it = m_elements . begin ( ) ; it ! = first_spread ; + + it ) {
2024-02-04 08:00:54 +01:00
if ( * it ) {
2026-02-11 22:42:53 +01:00
auto value = ( * it ) - > generate_bytecode ( generator ) . value ( ) ;
2024-06-01 10:00:32 +02:00
args . append ( generator . copy_if_needed_to_preserve_evaluation_order ( value ) ) ;
2024-05-08 12:43:08 +02:00
} else {
2025-04-04 23:16:34 +02:00
args . append ( generator . add_constant ( js_special_empty_value ( ) ) ) ;
2021-06-08 23:06:52 +02:00
}
}
2022-09-09 15:23:02 +02:00
2024-02-04 08:00:54 +01:00
auto dst = choose_dst ( generator , preferred_dst ) ;
if ( first_spread . index ( ) ! = 0 ) {
2024-05-08 12:43:08 +02:00
generator . emit_with_extra_operand_slots < Bytecode : : Op : : NewArray > ( args . size ( ) , dst , args ) ;
2024-02-04 08:00:54 +01:00
} else {
2025-11-20 22:14:50 +01:00
generator . emit < Bytecode : : Op : : NewArray > ( dst , ReadonlySpan < ScopedOperand > { } ) ;
2024-02-04 08:00:54 +01:00
}
2022-09-09 15:23:02 +02:00
if ( first_spread ! = m_elements . end ( ) ) {
for ( auto it = first_spread ; it ! = m_elements . end ( ) ; + + it ) {
if ( ! * it ) {
2025-04-04 23:16:34 +02:00
generator . emit < Bytecode : : Op : : ArrayAppend > ( dst , generator . add_constant ( js_special_empty_value ( ) ) , false ) ;
2022-09-09 15:23:02 +02:00
} else {
2026-02-11 22:42:53 +01:00
auto value = ( * it ) - > generate_bytecode ( generator ) . value ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : ArrayAppend > ( dst , value , * it & & is < SpreadExpression > ( * * it ) ) ;
2022-09-09 15:23:02 +02:00
}
}
2022-03-14 14:48:42 +00:00
}
2022-09-09 15:23:02 +02:00
2024-02-04 08:00:54 +01:00
return dst ;
2021-06-08 23:06:52 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > MemberExpression : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2021-06-04 21:03:53 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2026-02-15 12:16:19 +01:00
auto reference = generator . emit_load_from_reference ( * this , preferred_dst , Bytecode : : Generator : : ReferenceMode : : LoadOnly ) ;
2024-02-04 08:00:54 +01:00
return reference . loaded_value ;
2021-06-04 21:03:53 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > FunctionDeclaration : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-05 15:15:30 +02:00
{
2022-02-12 19:48:45 +03:30
if ( m_is_hoisted ) {
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2022-02-12 19:48:45 +03:30
auto index = generator . intern_identifier ( name ( ) ) ;
2024-05-07 21:36:56 +02:00
auto value = generator . allocate_register ( ) ;
2024-05-14 11:32:04 +02:00
generator . emit < Bytecode : : Op : : GetBinding > ( value , index ) ;
2024-05-14 11:30:30 +02:00
generator . emit < Bytecode : : Op : : SetVariableBinding > ( index , value ) ;
2022-02-12 19:48:45 +03:30
}
2024-05-07 21:36:56 +02:00
return Optional < ScopedOperand > { } ;
2021-06-05 15:15:30 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > FunctionExpression : : generate_bytecode_with_lhs_name ( Bytecode : : Generator & generator , Optional < Bytecode : : IdentifierTableIndex > lhs_name , Optional < ScopedOperand > preferred_dst , bool is_method ) const
2021-06-11 10:46:46 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2022-06-11 23:24:55 +01:00
bool has_name = ! name ( ) . is_empty ( ) ;
Optional < Bytecode : : IdentifierTableIndex > name_identifier ;
if ( has_name ) {
2023-06-16 16:34:47 +02:00
generator . begin_variable_scope ( ) ;
2022-06-11 23:24:55 +01:00
name_identifier = generator . intern_identifier ( name ( ) ) ;
2025-11-20 22:14:50 +01:00
generator . emit < Bytecode : : Op : : CreateVariable > ( * name_identifier , Bytecode : : Op : : EnvironmentMode : : Lexical , true , false , false ) ;
2022-06-11 23:24:55 +01:00
}
2024-02-04 08:00:54 +01:00
auto new_function = choose_dst ( generator , preferred_dst ) ;
2025-12-17 00:22:20 -06:00
generator . emit_new_function ( new_function , * this , lhs_name , is_method ) ;
2022-06-11 23:24:55 +01:00
if ( has_name ) {
2024-05-14 11:30:30 +02:00
generator . emit < Bytecode : : Op : : InitializeLexicalBinding > ( * name_identifier , new_function ) ;
2022-06-11 23:24:55 +01:00
generator . end_variable_scope ( ) ;
}
2024-02-04 08:00:54 +01:00
return new_function ;
2021-06-11 10:46:46 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > FunctionExpression : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2023-06-23 14:27:42 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2024-02-04 08:00:54 +01:00
return generate_bytecode_with_lhs_name ( generator , { } , preferred_dst ) ;
2023-06-23 14:27:42 +02:00
}
2026-02-11 22:42:53 +01:00
static void generate_object_binding_pattern_bytecode ( Bytecode : : Generator & generator , BindingPattern const & pattern , Bytecode : : Op : : BindingInitializationMode initialization_mode , ScopedOperand const & object )
2021-06-13 12:24:55 -07:00
{
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : ThrowIfNullish > ( object ) ;
2023-06-25 08:50:07 +02:00
2024-05-07 21:36:56 +02:00
Vector < ScopedOperand > excluded_property_names ;
2021-06-13 15:30:32 -07:00
auto has_rest = false ;
if ( pattern . entries . size ( ) > 0 )
has_rest = pattern . entries [ pattern . entries . size ( ) - 1 ] . is_rest ;
2021-06-13 12:24:55 -07:00
for ( auto & [ name , alias , initializer , is_rest ] : pattern . entries ) {
2021-06-13 15:30:32 -07:00
if ( is_rest ) {
VERIFY ( ! initializer ) ;
2023-07-02 11:24:55 +02:00
if ( name . has < NonnullRefPtr < Identifier const > > ( ) ) {
2023-07-05 02:17:10 +02:00
auto identifier = name . get < NonnullRefPtr < Identifier const > > ( ) ;
2021-06-13 15:30:32 -07:00
2024-05-07 21:36:56 +02:00
auto copy = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit_with_extra_operand_slots < Bytecode : : Op : : CopyObjectExcludingProperties > (
excluded_property_names . size ( ) , copy , object , excluded_property_names ) ;
generator . emit_set_variable ( * identifier , copy , initialization_mode ) ;
2021-06-13 15:30:32 -07:00
2026-02-11 22:42:53 +01:00
return ;
2023-07-02 11:24:55 +02:00
}
if ( alias . has < NonnullRefPtr < MemberExpression const > > ( ) ) {
2024-05-07 21:36:56 +02:00
auto copy = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit_with_extra_operand_slots < Bytecode : : Op : : CopyObjectExcludingProperties > (
excluded_property_names . size ( ) , copy , object , excluded_property_names ) ;
2026-02-11 22:42:53 +01:00
generator . emit_store_to_reference ( alias . get < NonnullRefPtr < MemberExpression const > > ( ) , copy ) ;
return ;
2023-07-02 11:24:55 +02:00
}
VERIFY_NOT_REACHED ( ) ;
2021-06-13 15:30:32 -07:00
}
2021-06-13 12:24:55 -07:00
2024-05-07 21:36:56 +02:00
auto value = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
2023-02-19 22:07:52 +01:00
if ( name . has < NonnullRefPtr < Identifier const > > ( ) ) {
2024-03-03 11:34:36 +01:00
auto const & identifier = name . get < NonnullRefPtr < Identifier const > > ( ) - > string ( ) ;
2021-06-13 15:30:32 -07:00
if ( has_rest ) {
2024-05-14 14:28:22 +02:00
excluded_property_names . append ( generator . add_constant ( PrimitiveString : : create ( generator . vm ( ) , identifier ) ) ) ;
2021-06-13 15:30:32 -07:00
}
2025-12-11 07:57:09 -06:00
generator . emit_get_by_id ( value , object , generator . intern_property_key ( identifier ) ) ;
2021-06-13 13:40:48 -07:00
} else {
2023-02-19 22:07:52 +01:00
auto expression = name . get < NonnullRefPtr < Expression const > > ( ) ;
2026-02-11 22:42:53 +01:00
auto property_name = expression - > generate_bytecode ( generator ) . value ( ) ;
2021-06-13 15:30:32 -07:00
if ( has_rest ) {
2024-06-01 10:00:32 +02:00
auto excluded_name = generator . copy_if_needed_to_preserve_evaluation_order ( property_name ) ;
2024-02-04 08:00:54 +01:00
excluded_property_names . append ( excluded_name ) ;
2021-06-13 15:30:32 -07:00
}
2025-04-03 14:18:24 +02:00
generator . emit_get_by_value ( value , object , property_name ) ;
2021-06-13 13:40:48 -07:00
}
2021-06-13 12:24:55 -07:00
2021-06-13 13:40:48 -07:00
if ( initializer ) {
auto & if_undefined_block = generator . make_block ( ) ;
auto & if_not_undefined_block = generator . make_block ( ) ;
2021-06-13 12:24:55 -07:00
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : JumpUndefined > (
2024-02-04 08:00:54 +01:00
value ,
2021-06-13 13:40:48 -07:00
Bytecode : : Label { if_undefined_block } ,
Bytecode : : Label { if_not_undefined_block } ) ;
2021-06-13 12:24:55 -07:00
2021-06-13 13:40:48 -07:00
generator . switch_to_basic_block ( if_undefined_block ) ;
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > default_value ;
2023-06-27 14:41:09 +02:00
if ( auto const * alias_identifier = alias . get_pointer < NonnullRefPtr < Identifier const > > ( ) ) {
2026-02-11 22:42:53 +01:00
default_value = generator . emit_named_evaluation_if_anonymous_function ( * initializer , generator . intern_identifier ( ( * alias_identifier ) - > string ( ) ) ) ;
2023-06-27 14:41:09 +02:00
} else if ( auto const * lhs = name . get_pointer < NonnullRefPtr < Identifier const > > ( ) ) {
2026-02-11 22:42:53 +01:00
default_value = generator . emit_named_evaluation_if_anonymous_function ( * initializer , generator . intern_identifier ( ( * lhs ) - > string ( ) ) ) ;
2023-06-23 14:27:42 +02:00
} else {
2026-02-11 22:42:53 +01:00
default_value = initializer - > generate_bytecode ( generator ) . value ( ) ;
2023-06-23 14:27:42 +02:00
}
2025-03-28 00:56:57 +01:00
generator . emit_mov ( value , * default_value ) ;
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { if_not_undefined_block } ) ;
2021-06-13 12:24:55 -07:00
2021-06-13 13:40:48 -07:00
generator . switch_to_basic_block ( if_not_undefined_block ) ;
}
2021-06-13 12:24:55 -07:00
2023-02-19 22:07:52 +01:00
if ( alias . has < NonnullRefPtr < BindingPattern const > > ( ) ) {
auto & binding_pattern = * alias . get < NonnullRefPtr < BindingPattern const > > ( ) ;
2024-06-01 10:00:32 +02:00
auto nested_value = generator . copy_if_needed_to_preserve_evaluation_order ( value ) ;
2026-02-11 22:42:53 +01:00
binding_pattern . generate_bytecode ( generator , initialization_mode , nested_value ) ;
2021-06-13 13:40:48 -07:00
} else if ( alias . has < Empty > ( ) ) {
2026-02-11 22:05:55 +01:00
// NB: Computed property names always require an alias, so name can't be an Expression here.
VERIFY ( ! name . has < NonnullRefPtr < Expression const > > ( ) ) ;
2021-06-13 13:40:48 -07:00
2023-07-05 02:17:10 +02:00
auto const & identifier = * name . get < NonnullRefPtr < Identifier const > > ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit_set_variable ( identifier , value , initialization_mode ) ;
2023-06-25 16:24:05 +02:00
} else if ( alias . has < NonnullRefPtr < MemberExpression const > > ( ) ) {
2026-02-11 22:42:53 +01:00
generator . emit_store_to_reference ( alias . get < NonnullRefPtr < MemberExpression const > > ( ) , value ) ;
2021-06-13 12:24:55 -07:00
} else {
2023-07-05 02:17:10 +02:00
auto const & identifier = * alias . get < NonnullRefPtr < Identifier const > > ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit_set_variable ( identifier , value , initialization_mode ) ;
2021-06-13 13:40:48 -07:00
}
}
}
2026-02-11 22:42:53 +01:00
static void generate_array_binding_pattern_bytecode ( Bytecode : : Generator & generator , BindingPattern const & pattern , Bytecode : : Op : : BindingInitializationMode initialization_mode , ScopedOperand const & input_array , [[maybe_unused]] Optional < ScopedOperand > preferred_dst = { } )
2021-06-13 13:40:48 -07:00
{
/*
* Consider the following destructuring assignment :
*
* let [ a , b , c , d , e ] = o ;
*
* It would be fairly trivial to just loop through this iterator , getting the value
* at each step and assigning them to the binding sequentially . However , this is not
* correct : once an iterator is exhausted , it must not be called again . This complicates
* the bytecode . In order to accomplish this , we do the following :
*
* - Reserve a special boolean register which holds ' true ' if the iterator is exhausted ,
* and false otherwise
* - When we are retrieving the value which should be bound , we first check this register .
2024-02-04 08:00:54 +01:00
* If it is ' true ' , we load undefined . Otherwise , we grab the next value from the iterator .
2021-06-13 13:40:48 -07:00
*
* Note that the is_exhausted register does not need to be loaded with false because the
* first IteratorNext bytecode is _not_ proceeded by an exhausted check , as it is
* unnecessary .
*/
2024-05-07 21:36:56 +02:00
auto is_iterator_exhausted = generator . allocate_register ( ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( is_iterator_exhausted , generator . add_constant ( Value ( false ) ) ) ;
2021-06-13 13:40:48 -07:00
2025-10-27 19:46:54 +01:00
auto iterator_object = generator . allocate_register ( ) ;
auto iterator_next_method = generator . allocate_register ( ) ;
auto iterator_done_property = generator . allocate_register ( ) ;
2025-11-20 22:14:50 +01:00
generator . emit < Bytecode : : Op : : GetIterator > ( iterator_object , iterator_next_method , iterator_done_property , input_array , IteratorHint : : Sync ) ;
2021-06-13 13:40:48 -07:00
bool first = true ;
2024-05-07 21:36:56 +02:00
auto assign_value_to_alias = [ & ] ( auto & alias , ScopedOperand value ) {
2022-02-12 19:54:08 +03:30
return alias . visit (
2026-02-11 22:42:53 +01:00
[ & ] ( Empty ) - > void {
2021-06-13 14:06:26 -07:00
// This element is an elision
} ,
2026-02-11 22:42:53 +01:00
[ & ] ( NonnullRefPtr < Identifier const > const & identifier ) - > void {
2024-02-04 08:00:54 +01:00
generator . emit_set_variable ( * identifier , value , initialization_mode ) ;
2021-06-13 14:06:26 -07:00
} ,
2026-02-11 22:42:53 +01:00
[ & ] ( NonnullRefPtr < BindingPattern const > const & pattern ) - > void {
pattern - > generate_bytecode ( generator , initialization_mode , value ) ;
2021-09-18 01:11:32 +02:00
} ,
2026-02-11 22:42:53 +01:00
[ & ] ( NonnullRefPtr < MemberExpression const > const & expr ) - > void {
generator . emit_store_to_reference ( * expr , value ) ;
2021-06-13 14:06:26 -07:00
} ) ;
} ;
2021-06-13 13:40:48 -07:00
for ( auto & [ name , alias , initializer , is_rest ] : pattern . entries ) {
VERIFY ( name . has < Empty > ( ) ) ;
2021-06-13 14:06:26 -07:00
if ( is_rest ) {
2022-07-17 19:25:23 +01:00
VERIFY ( ! initializer ) ;
2026-02-15 12:24:34 +01:00
// 13.15.5.3 AssignmentRestElement : ... DestructuringAssignmentTarget
// Step 1: If DestructuringAssignmentTarget is not ObjectLiteral or ArrayLiteral,
// let lref be ? Evaluation of DestructuringAssignmentTarget.
// The reference must be evaluated BEFORE iterating the remaining elements.
Optional < Bytecode : : Generator : : ReferenceOperands > lref ;
if ( auto const * member_expr = alias . get_pointer < NonnullRefPtr < MemberExpression const > > ( ) )
lref = generator . emit_evaluate_reference ( * * member_expr ) ;
2024-05-07 21:36:56 +02:00
auto value = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
2021-06-13 14:06:26 -07:00
if ( first ) {
// The iterator has not been called, and is thus known to be not exhausted
2025-10-27 19:46:54 +01:00
generator . emit < Bytecode : : Op : : IteratorToArray > ( value , iterator_object , iterator_next_method , iterator_done_property ) ;
2021-06-13 14:06:26 -07:00
} else {
auto & if_exhausted_block = generator . make_block ( ) ;
auto & if_not_exhausted_block = generator . make_block ( ) ;
auto & continuation_block = generator . make_block ( ) ;
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
is_iterator_exhausted ,
2021-06-13 14:06:26 -07:00
Bytecode : : Label { if_exhausted_block } ,
Bytecode : : Label { if_not_exhausted_block } ) ;
2024-05-07 21:36:56 +02:00
value = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
2021-06-13 14:06:26 -07:00
generator . switch_to_basic_block ( if_exhausted_block ) ;
2025-11-20 22:14:50 +01:00
generator . emit < Bytecode : : Op : : NewArray > ( value , ReadonlySpan < ScopedOperand > { } ) ;
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { continuation_block } ) ;
2021-06-13 14:06:26 -07:00
generator . switch_to_basic_block ( if_not_exhausted_block ) ;
2025-10-27 19:46:54 +01:00
generator . emit < Bytecode : : Op : : IteratorToArray > ( value , iterator_object , iterator_next_method , iterator_done_property ) ;
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { continuation_block } ) ;
2021-06-13 14:06:26 -07:00
generator . switch_to_basic_block ( continuation_block ) ;
}
2026-02-15 12:24:34 +01:00
if ( lref . has_value ( ) )
generator . emit_store_to_reference ( * lref , value ) ;
else
assign_value_to_alias ( alias , value ) ;
return ;
2021-06-13 14:06:26 -07:00
}
2021-06-13 13:40:48 -07:00
2026-02-15 12:24:34 +01:00
// 13.15.5.5 AssignmentElement : DestructuringAssignmentTarget Initializer(opt)
// Step 1: If DestructuringAssignmentTarget is not ObjectLiteral or ArrayLiteral,
// let lref be ? Evaluation of DestructuringAssignmentTarget.
// The reference must be evaluated BEFORE calling IteratorStepValue.
Optional < Bytecode : : Generator : : ReferenceOperands > lref ;
if ( auto const * member_expr = alias . get_pointer < NonnullRefPtr < MemberExpression const > > ( ) )
lref = generator . emit_evaluate_reference ( * * member_expr ) ;
2021-06-13 13:40:48 -07:00
auto & iterator_is_exhausted_block = generator . make_block ( ) ;
if ( ! first ) {
auto & iterator_is_not_exhausted_block = generator . make_block ( ) ;
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
is_iterator_exhausted ,
2021-06-13 13:40:48 -07:00
Bytecode : : Label { iterator_is_exhausted_block } ,
Bytecode : : Label { iterator_is_not_exhausted_block } ) ;
generator . switch_to_basic_block ( iterator_is_not_exhausted_block ) ;
2021-06-13 12:24:55 -07:00
}
2021-06-13 13:40:48 -07:00
2025-05-01 16:05:24 +03:00
auto value = generator . allocate_register ( ) ;
2025-10-27 19:46:54 +01:00
generator . emit < Bytecode : : Op : : IteratorNextUnpack > ( value , is_iterator_exhausted , iterator_object , iterator_next_method , iterator_done_property ) ;
2021-06-13 13:40:48 -07:00
// We still have to check for exhaustion here. If the iterator is exhausted,
// we need to bail before trying to get the value
auto & no_bail_block = generator . make_block ( ) ;
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
is_iterator_exhausted ,
2021-06-13 13:40:48 -07:00
Bytecode : : Label { iterator_is_exhausted_block } ,
Bytecode : : Label { no_bail_block } ) ;
generator . switch_to_basic_block ( no_bail_block ) ;
auto & create_binding_block = generator . make_block ( ) ;
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { create_binding_block } ) ;
2021-06-13 13:40:48 -07:00
// The iterator is exhausted, so we just load undefined and continue binding
generator . switch_to_basic_block ( iterator_is_exhausted_block ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( value , generator . add_constant ( js_undefined ( ) ) ) ;
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { create_binding_block } ) ;
2021-06-13 13:40:48 -07:00
generator . switch_to_basic_block ( create_binding_block ) ;
2022-07-17 19:25:23 +01:00
if ( initializer ) {
auto & value_is_undefined_block = generator . make_block ( ) ;
auto & value_is_not_undefined_block = generator . make_block ( ) ;
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : JumpUndefined > (
2024-02-04 08:00:54 +01:00
value ,
2022-07-17 19:25:23 +01:00
Bytecode : : Label { value_is_undefined_block } ,
Bytecode : : Label { value_is_not_undefined_block } ) ;
generator . switch_to_basic_block ( value_is_undefined_block ) ;
2023-06-23 14:27:42 +02:00
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > default_value ;
2023-06-25 11:11:36 +02:00
if ( auto const * alias_identifier = alias . get_pointer < NonnullRefPtr < Identifier const > > ( ) ) {
2026-02-11 22:42:53 +01:00
default_value = generator . emit_named_evaluation_if_anonymous_function ( * initializer , generator . intern_identifier ( ( * alias_identifier ) - > string ( ) ) ) ;
2023-06-25 11:11:36 +02:00
} else if ( auto const * name_identifier = name . get_pointer < NonnullRefPtr < Identifier const > > ( ) ) {
2026-02-11 22:42:53 +01:00
default_value = generator . emit_named_evaluation_if_anonymous_function ( * initializer , generator . intern_identifier ( ( * name_identifier ) - > string ( ) ) ) ;
2023-06-23 14:27:42 +02:00
} else {
2026-02-11 22:42:53 +01:00
default_value = initializer - > generate_bytecode ( generator ) . value ( ) ;
2023-06-23 14:27:42 +02:00
}
2025-03-28 00:56:57 +01:00
generator . emit_mov ( value , * default_value ) ;
2022-07-17 19:25:23 +01:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { value_is_not_undefined_block } ) ;
generator . switch_to_basic_block ( value_is_not_undefined_block ) ;
}
2026-02-15 12:24:34 +01:00
if ( lref . has_value ( ) )
generator . emit_store_to_reference ( * lref , value ) ;
else
assign_value_to_alias ( alias , value ) ;
2021-06-13 13:40:48 -07:00
first = false ;
}
2022-02-12 19:54:08 +03:30
2023-06-26 17:21:12 +02:00
auto & done_block = generator . make_block ( ) ;
auto & not_done_block = generator . make_block ( ) ;
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
is_iterator_exhausted ,
2023-06-26 17:21:12 +02:00
Bytecode : : Label { done_block } ,
Bytecode : : Label { not_done_block } ) ;
generator . switch_to_basic_block ( not_done_block ) ;
2026-02-11 23:49:20 +01:00
generator . emit < Bytecode : : Op : : IteratorClose > ( iterator_object , iterator_next_method , iterator_done_property , Completion : : Type : : Normal , generator . add_constant ( js_undefined ( ) ) ) ;
2023-06-26 17:21:12 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { done_block } ) ;
generator . switch_to_basic_block ( done_block ) ;
2021-06-13 13:40:48 -07:00
}
2026-02-11 22:42:53 +01:00
void BindingPattern : : generate_bytecode ( Bytecode : : Generator & generator , Bytecode : : Op : : BindingInitializationMode initialization_mode , ScopedOperand const & input_value ) const
2021-06-13 13:40:48 -07:00
{
2024-05-09 05:00:07 +00:00
if ( kind = = Kind : : Object )
2025-04-22 00:33:54 +02:00
return generate_object_binding_pattern_bytecode ( generator , * this , initialization_mode , input_value ) ;
2022-02-12 19:54:08 +03:30
2025-04-22 00:33:54 +02:00
return generate_array_binding_pattern_bytecode ( generator , * this , initialization_mode , input_value ) ;
2022-02-12 19:54:08 +03:30
}
2021-06-13 12:24:55 -07:00
2026-02-11 22:42:53 +01:00
static void assign_value_to_variable_declarator ( Bytecode : : Generator & generator , VariableDeclarator const & declarator , VariableDeclaration const & declaration , ScopedOperand value )
2022-03-18 20:18:19 +03:30
{
2024-05-14 11:30:30 +02:00
auto initialization_mode = declaration . is_lexical_declaration ( ) ? Bytecode : : Op : : BindingInitializationMode : : Initialize : Bytecode : : Op : : BindingInitializationMode : : Set ;
2022-03-18 20:18:19 +03:30
2026-02-11 22:42:53 +01:00
declarator . target ( ) . visit (
[ & ] ( NonnullRefPtr < Identifier const > const & id ) - > void {
2024-02-04 08:00:54 +01:00
generator . emit_set_variable ( * id , value , initialization_mode ) ;
2022-03-18 20:18:19 +03:30
} ,
2026-02-11 22:42:53 +01:00
[ & ] ( NonnullRefPtr < BindingPattern const > const & pattern ) - > void {
pattern - > generate_bytecode ( generator , initialization_mode , value ) ;
2022-03-18 20:18:19 +03:30
} ) ;
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > VariableDeclaration : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-10 00:29:17 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2023-07-02 10:48:17 +02:00
2021-06-10 22:12:21 +02:00
for ( auto & declarator : m_declarations ) {
2024-03-01 12:54:50 +01:00
// NOTE: `var` declarations can have duplicates, but duplicate `let` or `const` bindings are a syntax error.
// Because of this, we can sink `let` and `const` directly into the preferred_dst if available.
// This is not safe for `var` since the preferred_dst may be used in the initializer.
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > init_dst ;
2024-03-01 12:54:50 +01:00
if ( declaration_kind ( ) ! = DeclarationKind : : Var ) {
if ( auto const * identifier = declarator - > target ( ) . get_pointer < NonnullRefPtr < Identifier const > > ( ) ) {
if ( ( * identifier ) - > is_local ( ) ) {
2025-04-25 17:10:47 +02:00
init_dst = generator . local ( ( * identifier ) - > local_index ( ) ) ;
2024-03-01 12:54:50 +01:00
}
2023-06-23 14:27:42 +02:00
}
2024-02-04 08:00:54 +01:00
}
if ( declarator - > init ( ) ) {
2026-02-11 22:42:53 +01:00
auto value = [ & ] ( ) - > ScopedOperand {
2024-02-04 08:00:54 +01:00
if ( auto const * lhs = declarator - > target ( ) . get_pointer < NonnullRefPtr < Identifier const > > ( ) ) {
2026-02-11 22:42:53 +01:00
return generator . emit_named_evaluation_if_anonymous_function ( * declarator - > init ( ) , generator . intern_identifier ( ( * lhs ) - > string ( ) ) , init_dst ) ;
2024-02-04 08:00:54 +01:00
} else {
2026-02-11 22:42:53 +01:00
return declarator - > init ( ) - > generate_bytecode ( generator , init_dst ) . value ( ) ;
2024-02-04 08:00:54 +01:00
}
2026-02-11 22:42:53 +01:00
} ( ) ;
assign_value_to_variable_declarator ( generator , declarator , * this , value ) ;
2023-06-17 15:16:30 +02:00
} else if ( m_declaration_kind ! = DeclarationKind : : Var ) {
2026-02-11 22:42:53 +01:00
assign_value_to_variable_declarator ( generator , declarator , * this , generator . add_constant ( js_undefined ( ) ) ) ;
2023-06-17 15:16:30 +02:00
}
2022-02-12 19:54:08 +03:30
2024-02-04 08:00:54 +01:00
if ( auto const * identifier = declarator - > target ( ) . get_pointer < NonnullRefPtr < Identifier const > > ( ) ) {
if ( ( * identifier ) - > is_local ( ) ) {
2025-04-25 17:10:47 +02:00
generator . set_local_initialized ( ( * identifier ) - > local_index ( ) ) ;
2024-02-04 08:00:54 +01:00
}
}
}
// NOTE: VariableDeclaration doesn't return a completion value.
2024-05-07 21:36:56 +02:00
return Optional < ScopedOperand > { } ;
2021-06-10 00:29:17 +02:00
}
2024-02-04 08:00:54 +01:00
struct BaseAndValue {
2024-05-07 21:36:56 +02:00
ScopedOperand base ;
ScopedOperand value ;
2024-02-04 08:00:54 +01:00
} ;
2026-02-11 22:42:53 +01:00
static BaseAndValue get_base_and_value_from_member_expression ( Bytecode : : Generator & generator , MemberExpression const & member_expression )
2023-06-17 14:50:23 +01:00
{
// https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
if ( is < SuperExpression > ( member_expression . object ( ) ) ) {
// 1. Let env be GetThisEnvironment().
// 2. Let actualThis be ? env.GetThisBinding().
2024-05-07 12:16:37 +02:00
auto this_value = generator . get_this ( ) ;
2023-06-17 14:50:23 +01:00
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > computed_property ;
2023-06-17 14:50:23 +01:00
if ( member_expression . is_computed ( ) ) {
// SuperProperty : super [ Expression ]
// 3. Let propertyNameReference be ? Evaluation of Expression.
// 4. Let propertyNameValue be ? GetValue(propertyNameReference).
2026-02-11 22:42:53 +01:00
computed_property = member_expression . property ( ) . generate_bytecode ( generator ) ;
2023-06-17 14:50:23 +01:00
}
// 5/7. Return ? MakeSuperPropertyReference(actualThis, propertyKey, strict).
// https://tc39.es/ecma262/#sec-makesuperpropertyreference
// 1. Let env be GetThisEnvironment().
// 2. Assert: env.HasSuperBinding() is true.
// 3. Let baseValue be ? env.GetSuperBase().
2024-05-07 21:36:56 +02:00
auto super_base = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : ResolveSuperBase > ( super_base ) ;
2024-05-07 21:36:56 +02:00
auto value = generator . allocate_register ( ) ;
2023-06-17 14:50:23 +01:00
// 4. Return the Reference Record { [[Base]]: baseValue, [[ReferencedName]]: propertyKey, [[Strict]]: strict, [[ThisValue]]: actualThis }.
2024-02-04 08:00:54 +01:00
if ( computed_property . has_value ( ) ) {
2023-06-17 14:50:23 +01:00
// 5. Let propertyKey be ? ToPropertyKey(propertyNameValue).
2025-04-03 14:18:24 +02:00
generator . emit_get_by_value_with_this ( value , super_base , * computed_property , this_value ) ;
2023-06-17 14:50:23 +01:00
} else {
// 3. Let propertyKey be StringValue of IdentifierName.
2025-12-11 07:57:09 -06:00
auto property_key_table_index = generator . intern_property_key ( as < Identifier > ( member_expression . property ( ) ) . string ( ) ) ;
generator . emit_get_by_id_with_this ( value , super_base , property_key_table_index , this_value ) ;
2023-06-17 14:50:23 +01:00
}
2024-02-04 08:00:54 +01:00
return BaseAndValue { this_value , value } ;
}
2026-02-11 22:42:53 +01:00
auto base = member_expression . object ( ) . generate_bytecode ( generator ) . value ( ) ;
2024-05-07 21:36:56 +02:00
auto value = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
if ( member_expression . is_computed ( ) ) {
2026-02-11 22:42:53 +01:00
auto property = member_expression . property ( ) . generate_bytecode ( generator ) . value ( ) ;
2025-04-03 14:18:24 +02:00
generator . emit_get_by_value ( value , base , property ) ;
2024-02-04 08:00:54 +01:00
} else if ( is < PrivateIdentifier > ( member_expression . property ( ) ) ) {
generator . emit < Bytecode : : Op : : GetPrivateById > (
value ,
base ,
2025-01-21 09:12:05 -05:00
generator . intern_identifier ( as < PrivateIdentifier > ( member_expression . property ( ) ) . string ( ) ) ) ;
2023-06-17 14:50:23 +01:00
} else {
2024-03-29 11:26:10 -04:00
auto base_identifier = generator . intern_identifier_for_expression ( member_expression . object ( ) ) ;
2025-12-11 07:57:09 -06:00
generator . emit_get_by_id ( value , base , generator . intern_property_key ( as < Identifier > ( member_expression . property ( ) ) . string ( ) ) , move ( base_identifier ) ) ;
2023-06-17 14:50:23 +01:00
}
2024-02-04 08:00:54 +01:00
return BaseAndValue { base , value } ;
2023-06-17 14:50:23 +01:00
}
2026-02-11 22:42:53 +01:00
static void generate_optional_chain ( Bytecode : : Generator & generator , OptionalChain const & optional_chain , ScopedOperand current_value , ScopedOperand current_base , [[maybe_unused]] Optional < ScopedOperand > preferred_dst = { } ) ;
2023-06-17 14:50:23 +01:00
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > CallExpression : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2021-06-05 15:15:30 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2021-06-05 15:15:30 +02:00
2023-11-17 11:48:30 +01:00
Optional < Bytecode : : Builtin > builtin ;
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > original_callee ;
2024-11-08 17:59:12 +01:00
auto original_this_value = generator . add_constant ( js_undefined ( ) ) ;
2025-11-06 19:00:36 +00:00
auto dst = choose_dst ( generator , preferred_dst ) ;
2024-10-31 22:47:30 +01:00
Bytecode : : Op : : CallType call_type = Bytecode : : Op : : CallType : : Call ;
2024-02-04 08:00:54 +01:00
2021-06-11 01:36:10 +04:30
if ( is < NewExpression > ( this ) ) {
2026-02-11 22:42:53 +01:00
original_callee = m_callee - > generate_bytecode ( generator ) . value ( ) ;
2024-10-31 22:47:30 +01:00
call_type = Bytecode : : Op : : CallType : : Construct ;
2021-06-11 01:36:10 +04:30
} else if ( is < MemberExpression > ( * m_callee ) ) {
2022-04-01 20:58:27 +03:00
auto & member_expression = static_cast < MemberExpression const & > ( * m_callee ) ;
2026-02-11 22:42:53 +01:00
auto base_and_value = get_base_and_value_from_member_expression ( generator , member_expression ) ;
2024-02-04 08:00:54 +01:00
original_callee = base_and_value . value ;
2024-11-08 17:59:12 +01:00
original_this_value = base_and_value . base ;
2023-11-17 11:48:30 +01:00
builtin = Bytecode : : get_builtin ( member_expression ) ;
2023-06-17 14:50:23 +01:00
} else if ( is < OptionalChain > ( * m_callee ) ) {
auto & optional_chain = static_cast < OptionalChain const & > ( * m_callee ) ;
2024-05-07 21:36:56 +02:00
original_callee = generator . allocate_register ( ) ;
2024-11-08 17:59:12 +01:00
original_this_value = generator . allocate_register ( ) ;
2026-02-11 22:42:53 +01:00
generate_optional_chain ( generator , optional_chain , * original_callee , original_this_value ) ;
2023-08-01 14:33:58 +02:00
} else if ( is < Identifier > ( * m_callee ) ) {
2024-02-04 08:00:54 +01:00
// If the original_callee is an identifier, we may need to extract a `this` value.
2023-08-01 14:33:58 +02:00
// This is important when we're inside a `with` statement and calling a method on
// the environment's binding object.
// NOTE: If the identifier refers to a known "local" or "global", we know it can't be
// a `with` binding, so we can skip this.
auto & identifier = static_cast < Identifier const & > ( * m_callee ) ;
2025-11-06 19:00:36 +00:00
if ( generator . builtin_abstract_operations_enabled ( ) & & identifier . is_global ( ) ) {
2026-02-11 22:42:53 +01:00
generator . generate_builtin_abstract_operation ( identifier , arguments ( ) , dst ) ;
2025-11-06 19:00:36 +00:00
return dst ;
}
2024-10-31 22:47:30 +01:00
if ( identifier . string ( ) = = " eval " sv ) {
call_type = Bytecode : : Op : : CallType : : DirectEval ;
}
2024-02-04 08:00:54 +01:00
if ( identifier . is_local ( ) ) {
2026-02-17 17:42:14 +01:00
generator . emit_tdz_check_if_needed ( identifier ) ;
original_callee = generator . local ( identifier . local_index ( ) ) ;
2024-02-04 08:00:54 +01:00
} else if ( identifier . is_global ( ) ) {
original_callee = m_callee - > generate_bytecode ( generator ) . value ( ) ;
2023-08-01 14:33:58 +02:00
} else {
2024-05-07 21:36:56 +02:00
original_callee = generator . allocate_register ( ) ;
2024-11-08 17:59:12 +01:00
original_this_value = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : GetCalleeAndThisFromEnvironment > (
* original_callee ,
2024-11-08 17:59:12 +01:00
original_this_value ,
2024-05-11 18:28:03 +02:00
generator . intern_identifier ( identifier . string ( ) ) ) ;
2023-08-01 14:33:58 +02:00
}
2021-06-11 01:36:10 +04:30
} else {
2026-02-08 12:14:57 +01:00
// NB: For non-Reference calls, EvaluateCall sets thisValue to undefined.
// OrdinaryCallBindThis coerces undefined to the global object in sloppy mode at runtime.
2026-02-11 22:42:53 +01:00
original_callee = m_callee - > generate_bytecode ( generator ) . value ( ) ;
2021-06-11 01:36:10 +04:30
}
2024-11-08 17:59:12 +01:00
// NOTE: If the callee/this value isn't already a temporary, we copy them to new registers
// to avoid overwriting them while evaluating arguments.
// Example: foo.bar(Object.getPrototypeOf(foo).bar = null, foo = null)
auto this_value = generator . copy_if_needed_to_preserve_evaluation_order ( original_this_value ) ;
2024-06-01 10:00:32 +02:00
auto callee = generator . copy_if_needed_to_preserve_evaluation_order ( original_callee . value ( ) ) ;
2024-02-04 08:00:54 +01:00
2022-10-01 01:36:06 +02:00
Optional < Bytecode : : StringTableIndex > expression_string_index ;
if ( auto expression_string = this - > expression_string ( ) ; expression_string . has_value ( ) )
2025-08-07 19:31:52 -04:00
expression_string_index = generator . intern_string ( expression_string . release_value ( ) ) ;
2022-10-01 01:36:06 +02:00
2023-07-02 16:33:00 +02:00
bool has_spread = any_of ( arguments ( ) , [ ] ( auto & argument ) { return argument . is_spread ; } ) ;
if ( has_spread ) {
2026-02-11 22:42:53 +01:00
auto arguments = arguments_to_array_for_call ( generator , this - > arguments ( ) ) . value ( ) ;
2025-08-30 11:00:54 +02:00
if ( call_type = = Op : : CallType : : Construct ) {
generator . emit < Bytecode : : Op : : CallConstructWithArgumentArray > ( dst , callee , this_value , arguments , expression_string_index ) ;
} else if ( call_type = = Op : : CallType : : DirectEval ) {
generator . emit < Bytecode : : Op : : CallDirectEvalWithArgumentArray > ( dst , callee , this_value , arguments , expression_string_index ) ;
} else {
generator . emit < Bytecode : : Op : : CallWithArgumentArray > ( dst , callee , this_value , arguments , expression_string_index ) ;
}
2023-07-02 16:33:00 +02:00
} else {
2024-05-07 21:36:56 +02:00
Vector < ScopedOperand > argument_operands ;
2024-05-08 12:43:08 +02:00
argument_operands . ensure_capacity ( arguments ( ) . size ( ) ) ;
2023-07-02 16:33:00 +02:00
for ( auto const & argument : arguments ( ) ) {
2026-02-11 22:42:53 +01:00
auto argument_value = argument . value - > generate_bytecode ( generator ) . value ( ) ;
2024-06-01 10:00:32 +02:00
argument_operands . append ( generator . copy_if_needed_to_preserve_evaluation_order ( argument_value ) ) ;
2023-07-02 16:33:00 +02:00
}
2025-12-04 21:24:16 +01:00
if ( builtin . has_value ( ) & & builtin_argument_count ( builtin . value ( ) ) = = argument_operands . size ( ) ) {
2024-10-31 22:47:30 +01:00
VERIFY ( call_type = = Op : : CallType : : Call ) ;
generator . emit_with_extra_operand_slots < Bytecode : : Op : : CallBuiltin > (
argument_operands . size ( ) ,
dst ,
callee ,
this_value ,
builtin . value ( ) ,
2025-11-20 22:14:50 +01:00
expression_string_index ,
argument_operands ) ;
2024-10-31 22:47:30 +01:00
} else if ( call_type = = Op : : CallType : : Construct ) {
generator . emit_with_extra_operand_slots < Bytecode : : Op : : CallConstruct > (
argument_operands . size ( ) ,
dst ,
callee ,
2025-11-20 22:14:50 +01:00
expression_string_index ,
argument_operands ) ;
2024-10-31 22:47:30 +01:00
} else if ( call_type = = Op : : CallType : : DirectEval ) {
generator . emit_with_extra_operand_slots < Bytecode : : Op : : CallDirectEval > (
argument_operands . size ( ) ,
dst ,
callee ,
this_value ,
2025-11-20 22:14:50 +01:00
expression_string_index ,
argument_operands ) ;
2024-10-31 22:47:30 +01:00
} else {
generator . emit_with_extra_operand_slots < Bytecode : : Op : : Call > (
argument_operands . size ( ) ,
dst ,
callee ,
this_value ,
2025-11-20 22:14:50 +01:00
expression_string_index ,
argument_operands ) ;
2024-10-31 22:47:30 +01:00
}
2023-07-02 16:33:00 +02:00
}
2022-10-01 01:36:06 +02:00
2024-02-04 08:00:54 +01:00
return dst ;
2021-06-05 15:15:30 +02:00
}
2024-05-07 21:36:56 +02:00
static ScopedOperand generate_await (
2024-02-04 08:00:54 +01:00
Bytecode : : Generator & generator ,
2024-05-07 21:36:56 +02:00
ScopedOperand argument ,
ScopedOperand received_completion ,
ScopedOperand received_completion_type ,
2025-03-31 09:32:39 +01:00
ScopedOperand received_completion_value ) ;
2023-08-09 22:12:07 +01:00
// https://tc39.es/ecma262/#sec-return-statement-runtime-semantics-evaluation
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ReturnStatement : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > ) const
2021-06-05 15:53:36 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2024-02-04 08:00:54 +01:00
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > return_value ;
2024-02-04 08:00:54 +01:00
2023-08-09 22:12:07 +01:00
if ( m_argument ) {
// ReturnStatement : return Expression ;
// 1. Let exprRef be ? Evaluation of Expression.
// 2. Let exprValue be ? GetValue(exprRef).
2026-02-11 22:42:53 +01:00
return_value = m_argument - > generate_bytecode ( generator ) . value ( ) ;
2023-08-09 22:12:07 +01:00
// 3. If GetGeneratorKind() is async, set exprValue to ? Await(exprValue).
// Spec Issue?: The spec doesn't seem to do implicit await on explicit return for async functions, but does for
// async generators. However, the major engines do so, and this is observable via constructor lookups
// on Promise objects and custom thenables.
// See: https://tc39.es/ecma262/#sec-asyncblockstart
// c. Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done.
if ( generator . is_in_async_function ( ) ) {
2024-05-07 21:36:56 +02:00
auto received_completion = generator . allocate_register ( ) ;
auto received_completion_type = generator . allocate_register ( ) ;
auto received_completion_value = generator . allocate_register ( ) ;
2025-03-31 09:32:39 +01:00
return_value = generate_await ( generator , * return_value , received_completion , received_completion_type , received_completion_value ) ;
2023-08-09 22:12:07 +01:00
}
// 4. Return Completion Record { [[Type]]: return, [[Value]]: exprValue, [[Target]]: empty }.
} else {
// ReturnStatement : return ;
// 1. Return Completion Record { [[Type]]: return, [[Value]]: undefined, [[Target]]: empty }.
2024-02-04 08:00:54 +01:00
return_value = generator . add_constant ( js_undefined ( ) ) ;
2023-08-09 22:12:07 +01:00
}
2021-06-11 01:38:30 +04:30
2024-05-12 11:03:26 +02:00
if ( generator . is_in_generator_or_async_function ( ) )
generator . emit_return < Bytecode : : Op : : Yield > ( return_value . value ( ) ) ;
else
generator . emit_return < Bytecode : : Op : : Return > ( return_value . value ( ) ) ;
2022-02-12 19:54:08 +03:30
2024-02-04 08:00:54 +01:00
return return_value ;
2021-06-11 01:38:30 +04:30
}
2024-02-04 08:00:54 +01:00
static void get_received_completion_type_and_value (
Bytecode : : Generator & generator ,
2024-05-07 21:36:56 +02:00
ScopedOperand received_completion ,
ScopedOperand received_completion_type ,
2025-03-31 09:32:39 +01:00
ScopedOperand received_completion_value )
2023-07-14 21:57:49 +01:00
{
2025-03-31 09:32:39 +01:00
generator . emit < Op : : GetCompletionFields > ( received_completion_type , received_completion_value , received_completion ) ;
2023-07-14 21:57:49 +01:00
}
enum class AwaitBeforeYield {
No ,
Yes ,
} ;
2024-02-04 08:00:54 +01:00
static void generate_yield ( Bytecode : : Generator & generator ,
Bytecode : : Label continuation_label ,
2024-05-07 21:36:56 +02:00
ScopedOperand argument ,
ScopedOperand received_completion ,
ScopedOperand received_completion_type ,
ScopedOperand received_completion_value ,
2024-02-04 08:00:54 +01:00
AwaitBeforeYield await_before_yield )
2023-07-14 21:57:49 +01:00
{
if ( ! generator . is_in_async_generator_function ( ) ) {
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Yield > ( Bytecode : : Label { continuation_label } , argument ) ;
2023-07-14 21:57:49 +01:00
return ;
}
if ( await_before_yield = = AwaitBeforeYield : : Yes )
2025-03-31 09:32:39 +01:00
argument = generate_await ( generator , argument , received_completion , received_completion_type , received_completion_value ) ;
2023-07-14 21:57:49 +01:00
auto & unwrap_yield_resumption_block = generator . make_block ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Yield > ( Bytecode : : Label { unwrap_yield_resumption_block } , argument ) ;
2023-07-14 21:57:49 +01:00
generator . switch_to_basic_block ( unwrap_yield_resumption_block ) ;
2024-02-04 08:00:54 +01:00
2025-03-28 00:56:57 +01:00
generator . emit_mov ( received_completion , generator . accumulator ( ) ) ;
2025-03-31 09:32:39 +01:00
get_received_completion_type_and_value ( generator , received_completion , received_completion_type , received_completion_value ) ;
2023-07-14 21:57:49 +01:00
// 27.6.3.7 AsyncGeneratorUnwrapYieldResumption ( resumptionValue ), https://tc39.es/ecma262/#sec-asyncgeneratorunwrapyieldresumption
// 1. If resumptionValue.[[Type]] is not return, return ? resumptionValue.
auto & resumption_value_type_is_return_block = generator . make_block ( ) ;
2024-05-07 21:36:56 +02:00
auto resumption_value_type_is_not_return_result = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : StrictlyInequals > (
resumption_value_type_is_not_return_result ,
received_completion_type ,
generator . add_constant ( Value ( to_underlying ( Completion : : Type : : Return ) ) ) ) ;
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
resumption_value_type_is_not_return_result ,
Bytecode : : Label { continuation_label } ,
2023-07-14 21:57:49 +01:00
Bytecode : : Label { resumption_value_type_is_return_block } ) ;
generator . switch_to_basic_block ( resumption_value_type_is_return_block ) ;
// 2. Let awaited be Completion(Await(resumptionValue.[[Value]])).
2025-03-31 09:32:39 +01:00
generate_await ( generator , received_completion_value , received_completion , received_completion_type , received_completion_value ) ;
2023-07-14 21:57:49 +01:00
// 3. If awaited.[[Type]] is throw, return ? awaited.
auto & awaited_type_is_normal_block = generator . make_block ( ) ;
2024-05-07 21:36:56 +02:00
auto awaited_type_is_throw_result = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : StrictlyEquals > (
awaited_type_is_throw_result ,
received_completion_type ,
generator . add_constant ( Value ( to_underlying ( Completion : : Type : : Throw ) ) ) ) ;
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
awaited_type_is_throw_result ,
Bytecode : : Label { continuation_label } ,
2023-07-14 21:57:49 +01:00
Bytecode : : Label { awaited_type_is_normal_block } ) ;
// 4. Assert: awaited.[[Type]] is normal.
generator . switch_to_basic_block ( awaited_type_is_normal_block ) ;
// 5. Return Completion Record { [[Type]]: return, [[Value]]: awaited.[[Value]], [[Target]]: empty }.
2025-03-31 09:32:39 +01:00
generator . emit < Bytecode : : Op : : SetCompletionType > ( received_completion , Completion : : Type : : Return ) ;
2023-07-14 21:57:49 +01:00
generator . emit < Bytecode : : Op : : Jump > ( continuation_label ) ;
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > YieldExpression : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-11 01:38:30 +04:30
{
2024-05-06 22:38:43 +02:00
// Note: We need to catch any scheduled exceptions and reschedule them on re-entry
// as the act of yielding would otherwise clear them out
// This only applies when we are in a finalizer
bool is_in_finalizer = generator . is_in_finalizer ( ) ;
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > saved_exception ;
2024-05-06 22:38:43 +02:00
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2021-06-11 01:38:30 +04:30
VERIFY ( generator . is_in_generator_function ( ) ) ;
2024-05-07 21:36:56 +02:00
auto received_completion = generator . allocate_register ( ) ;
auto received_completion_type = generator . allocate_register ( ) ;
auto received_completion_value = generator . allocate_register ( ) ;
2022-11-25 23:14:08 +00:00
2022-02-12 19:54:08 +03:30
if ( m_is_yield_from ) {
2022-12-09 18:48:57 +00:00
// 15.5.5 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-generator-function-definitions-runtime-semantics-evaluation
2023-07-14 21:57:49 +01:00
// 1. Let generatorKind be GetGeneratorKind().
// NOTE: is_in_async_generator_function differentiates the generator kind.
2022-12-09 18:48:57 +00:00
// 2. Let exprRef be ? Evaluation of AssignmentExpression.
// 3. Let value be ? GetValue(exprRef).
VERIFY ( m_argument ) ;
2026-02-11 22:42:53 +01:00
auto value = m_argument - > generate_bytecode ( generator ) . value ( ) ;
2022-12-09 18:48:57 +00:00
// 4. Let iteratorRecord be ? GetIterator(value, generatorKind).
// 5. Let iterator be iteratorRecord.[[Iterator]].
2024-05-07 21:36:56 +02:00
auto iterator = generator . allocate_register ( ) ;
auto next_method = generator . allocate_register ( ) ;
2025-10-27 19:46:54 +01:00
auto iterator_done_property = generator . allocate_register ( ) ;
auto iterator_hint = generator . is_in_async_generator_function ( ) ? IteratorHint : : Async : IteratorHint : : Sync ;
generator . emit < Bytecode : : Op : : GetIterator > ( iterator , next_method , iterator_done_property , value , iterator_hint ) ;
2022-12-09 18:48:57 +00:00
// 6. Let received be NormalCompletion(undefined).
// See get_received_completion_type_and_value above.
2025-03-28 00:56:57 +01:00
generator . emit_mov ( received_completion_type , generator . add_constant ( Value ( to_underlying ( Completion : : Type : : Normal ) ) ) ) ;
2022-12-09 18:48:57 +00:00
2025-03-28 00:56:57 +01:00
generator . emit_mov ( received_completion_value , generator . add_constant ( js_undefined ( ) ) ) ;
2022-12-09 18:48:57 +00:00
// 7. Repeat,
auto & loop_block = generator . make_block ( ) ;
auto & continuation_block = generator . make_block ( ) ;
auto & loop_end_block = generator . make_block ( ) ;
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { loop_block } ) ;
generator . switch_to_basic_block ( loop_block ) ;
// a. If received.[[Type]] is normal, then
auto & type_is_normal_block = generator . make_block ( ) ;
auto & is_type_throw_block = generator . make_block ( ) ;
2024-05-07 21:36:56 +02:00
auto received_completion_type_register_is_normal = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : StrictlyEquals > (
received_completion_type_register_is_normal ,
received_completion_type ,
generator . add_constant ( Value ( to_underlying ( Completion : : Type : : Normal ) ) ) ) ;
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
received_completion_type_register_is_normal ,
2022-12-09 18:48:57 +00:00
Bytecode : : Label { type_is_normal_block } ,
Bytecode : : Label { is_type_throw_block } ) ;
generator . switch_to_basic_block ( type_is_normal_block ) ;
// i. Let innerResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]], « received.[[Value]] »).
2024-05-07 21:36:56 +02:00
auto inner_result = generator . allocate_register ( ) ;
2025-12-28 17:48:01 +01:00
generator . emit_with_extra_operand_slots < Bytecode : : Op : : Call > ( 1 , inner_result , next_method , iterator , OptionalNone { } , ReadonlySpan < ScopedOperand > { & received_completion_value , 1 } ) ;
2022-12-09 18:48:57 +00:00
2023-07-14 21:57:49 +01:00
// ii. If generatorKind is async, set innerResult to ? Await(innerResult).
2024-02-04 08:00:54 +01:00
if ( generator . is_in_async_generator_function ( ) ) {
2025-03-31 09:32:39 +01:00
auto new_inner_result = generate_await ( generator , inner_result , received_completion , received_completion_type , received_completion_value ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( inner_result , new_inner_result ) ;
2024-02-04 08:00:54 +01:00
}
2022-12-09 18:48:57 +00:00
// iii. If innerResult is not an Object, throw a TypeError exception.
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : ThrowIfNotObject > ( inner_result ) ;
2022-12-09 18:48:57 +00:00
// iv. Let done be ? IteratorComplete(innerResult).
2024-05-07 21:36:56 +02:00
auto done = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit_iterator_complete ( done , inner_result ) ;
2022-12-09 18:48:57 +00:00
// v. If done is true, then
auto & type_is_normal_done_block = generator . make_block ( ) ;
auto & type_is_normal_not_done_block = generator . make_block ( ) ;
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
done ,
2022-12-09 18:48:57 +00:00
Bytecode : : Label { type_is_normal_done_block } ,
Bytecode : : Label { type_is_normal_not_done_block } ) ;
generator . switch_to_basic_block ( type_is_normal_done_block ) ;
// 1. Return ? IteratorValue(innerResult).
2024-05-07 21:36:56 +02:00
auto return_value = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit_iterator_value ( return_value , inner_result ) ;
2022-12-09 18:48:57 +00:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { loop_end_block } ) ;
generator . switch_to_basic_block ( type_is_normal_not_done_block ) ;
2023-07-14 21:57:49 +01:00
// vi. If generatorKind is async, set received to Completion(AsyncGeneratorYield(? IteratorValue(innerResult))).
2022-12-09 18:48:57 +00:00
// vii. Else, set received to Completion(GeneratorYield(innerResult)).
2024-02-04 08:00:54 +01:00
{
// FIXME: Yield currently only accepts a Value, not an object conforming to the IteratorResult interface, so we have to do an observable lookup of `value` here.
// This only matters for non-async generators.
2024-05-07 21:36:56 +02:00
auto current_value = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit_iterator_value ( current_value , inner_result ) ;
2024-05-06 22:38:43 +02:00
if ( is_in_finalizer ) {
saved_exception = generator . allocate_register ( ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( Bytecode : : Operand ( * saved_exception ) , Bytecode : : Operand ( Bytecode : : Register : : exception ( ) ) ) ;
2024-05-06 22:38:43 +02:00
}
2024-02-04 08:00:54 +01:00
generate_yield ( generator ,
Bytecode : : Label { continuation_block } ,
current_value ,
received_completion ,
received_completion_type ,
received_completion_value ,
AwaitBeforeYield : : No ) ;
}
2022-12-09 18:48:57 +00:00
// b. Else if received.[[Type]] is throw, then
generator . switch_to_basic_block ( is_type_throw_block ) ;
auto & type_is_throw_block = generator . make_block ( ) ;
auto & type_is_return_block = generator . make_block ( ) ;
2024-05-07 21:36:56 +02:00
auto received_completion_type_register_is_throw = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : StrictlyEquals > (
received_completion_type_register_is_throw ,
2024-05-07 21:36:56 +02:00
received_completion_type ,
2024-02-04 08:00:54 +01:00
generator . add_constant ( Value ( to_underlying ( Completion : : Type : : Throw ) ) ) ) ;
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
received_completion_type_register_is_throw ,
2022-12-09 18:48:57 +00:00
Bytecode : : Label { type_is_throw_block } ,
Bytecode : : Label { type_is_return_block } ) ;
generator . switch_to_basic_block ( type_is_throw_block ) ;
// i. Let throw be ? GetMethod(iterator, "throw").
2024-05-07 21:36:56 +02:00
auto throw_method = generator . allocate_register ( ) ;
2025-12-11 07:57:09 -06:00
generator . emit < Bytecode : : Op : : GetMethod > ( throw_method , iterator , generator . intern_property_key ( " throw " _utf16_fly_string ) ) ;
2022-12-09 18:48:57 +00:00
// ii. If throw is not undefined, then
auto & throw_method_is_defined_block = generator . make_block ( ) ;
auto & throw_method_is_undefined_block = generator . make_block ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : JumpUndefined > (
throw_method ,
Bytecode : : Label { throw_method_is_undefined_block } ,
Bytecode : : Label { throw_method_is_defined_block } ) ;
2022-12-09 18:48:57 +00:00
generator . switch_to_basic_block ( throw_method_is_defined_block ) ;
// 1. Let innerResult be ? Call(throw, iterator, « received.[[Value]] »).
2025-12-28 17:48:01 +01:00
generator . emit_with_extra_operand_slots < Bytecode : : Op : : Call > ( 1 , inner_result , throw_method , iterator , OptionalNone { } , ReadonlySpan < ScopedOperand > { & received_completion_value , 1 } ) ;
2022-12-09 18:48:57 +00:00
2023-07-14 21:57:49 +01:00
// 2. If generatorKind is async, set innerResult to ? Await(innerResult).
2024-02-04 08:00:54 +01:00
if ( generator . is_in_async_generator_function ( ) ) {
2025-03-31 09:32:39 +01:00
auto new_result = generate_await ( generator , inner_result , received_completion , received_completion_type , received_completion_value ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( inner_result , new_result ) ;
2024-02-04 08:00:54 +01:00
}
2022-12-09 18:48:57 +00:00
// 3. NOTE: Exceptions from the inner iterator throw method are propagated. Normal completions from an inner throw method are processed similarly to an inner next.
// 4. If innerResult is not an Object, throw a TypeError exception.
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : ThrowIfNotObject > ( inner_result ) ;
2022-12-09 18:48:57 +00:00
// 5. Let done be ? IteratorComplete(innerResult).
2024-02-04 08:00:54 +01:00
generator . emit_iterator_complete ( done , inner_result ) ;
2022-12-09 18:48:57 +00:00
// 6. If done is true, then
auto & type_is_throw_done_block = generator . make_block ( ) ;
auto & type_is_throw_not_done_block = generator . make_block ( ) ;
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
done ,
2022-12-09 18:48:57 +00:00
Bytecode : : Label { type_is_throw_done_block } ,
Bytecode : : Label { type_is_throw_not_done_block } ) ;
generator . switch_to_basic_block ( type_is_throw_done_block ) ;
// a. Return ? IteratorValue(innerResult).
2024-02-04 08:00:54 +01:00
generator . emit_iterator_value ( return_value , inner_result ) ;
2022-12-09 18:48:57 +00:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { loop_end_block } ) ;
generator . switch_to_basic_block ( type_is_throw_not_done_block ) ;
2024-02-04 08:00:54 +01:00
{
// 7. If generatorKind is async, set received to Completion(AsyncGeneratorYield(? IteratorValue(innerResult))).
// 8. Else, set received to Completion(GeneratorYield(innerResult)).
2022-12-09 18:48:57 +00:00
2024-02-04 08:00:54 +01:00
// FIXME: Yield currently only accepts a Value, not an object conforming to the IteratorResult interface, so we have to do an observable lookup of `value` here.
// This only matters for non-async generators.
2024-05-07 21:36:56 +02:00
auto yield_value = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit_iterator_value ( yield_value , inner_result ) ;
2025-03-31 09:32:39 +01:00
generate_yield ( generator , Bytecode : : Label { continuation_block } , yield_value , received_completion , received_completion_type , received_completion_value , AwaitBeforeYield : : No ) ;
2024-02-04 08:00:54 +01:00
}
2022-12-09 18:48:57 +00:00
generator . switch_to_basic_block ( throw_method_is_undefined_block ) ;
// 1. NOTE: If iterator does not have a throw method, this throw is going to terminate the yield* loop. But first we need to give iterator a chance to clean up.
// 2. Let closeCompletion be Completion Record { [[Type]]: normal, [[Value]]: empty, [[Target]]: empty }.
2023-07-14 21:57:49 +01:00
// 3. If generatorKind is async, perform ? AsyncIteratorClose(iteratorRecord, closeCompletion).
if ( generator . is_in_async_generator_function ( ) ) {
2026-02-12 01:29:28 +01:00
// Inline AsyncIteratorClose with proper Await op to avoid
// spinning the event loop synchronously.
auto return_method = generator . allocate_register ( ) ;
generator . emit < Bytecode : : Op : : GetMethod > ( return_method , iterator , generator . intern_property_key ( " return " _utf16_fly_string ) ) ;
auto & call_return_block = generator . make_block ( ) ;
auto & after_close = generator . make_block ( ) ;
generator . emit < Bytecode : : Op : : JumpUndefined > ( return_method , Bytecode : : Label { after_close } , Bytecode : : Label { call_return_block } ) ;
generator . switch_to_basic_block ( call_return_block ) ;
auto inner_result = generator . allocate_register ( ) ;
generator . emit_with_extra_operand_slots < Bytecode : : Op : : Call > ( 0 , inner_result , return_method , iterator , OptionalNone { } , ReadonlySpan < ScopedOperand > { } ) ;
auto awaited = generate_await ( generator , inner_result , received_completion , received_completion_type , received_completion_value ) ;
generator . emit < Bytecode : : Op : : ThrowIfNotObject > ( awaited ) ;
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { after_close } ) ;
generator . switch_to_basic_block ( after_close ) ;
2023-07-14 21:57:49 +01:00
}
// 4. Else, perform ? IteratorClose(iteratorRecord, closeCompletion).
else {
2026-02-11 23:49:20 +01:00
generator . emit < Bytecode : : Op : : IteratorClose > ( iterator , next_method , done , Completion : : Type : : Normal , generator . add_constant ( js_undefined ( ) ) ) ;
2023-07-14 21:57:49 +01:00
}
2022-12-09 18:48:57 +00:00
// 5. NOTE: The next step throws a TypeError to indicate that there was a yield* protocol violation: iterator does not have a throw method.
// 6. Throw a TypeError exception.
2024-05-07 21:36:56 +02:00
auto exception = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : NewTypeError > ( exception , generator . intern_string ( ErrorType : : YieldFromIteratorMissingThrowMethod . message ( ) ) ) ;
2022-12-09 18:48:57 +00:00
generator . perform_needed_unwinds < Bytecode : : Op : : Throw > ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Throw > ( exception ) ;
2022-12-09 18:48:57 +00:00
// c. Else,
// i. Assert: received.[[Type]] is return.
generator . switch_to_basic_block ( type_is_return_block ) ;
// ii. Let return be ? GetMethod(iterator, "return").
2024-05-07 21:36:56 +02:00
auto return_method = generator . allocate_register ( ) ;
2025-12-11 07:57:09 -06:00
generator . emit < Bytecode : : Op : : GetMethod > ( return_method , iterator , generator . intern_property_key ( " return " _utf16_fly_string ) ) ;
2022-12-09 18:48:57 +00:00
// iii. If return is undefined, then
auto & return_is_undefined_block = generator . make_block ( ) ;
auto & return_is_defined_block = generator . make_block ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : JumpUndefined > (
return_method ,
2022-12-09 18:48:57 +00:00
Bytecode : : Label { return_is_undefined_block } ,
Bytecode : : Label { return_is_defined_block } ) ;
generator . switch_to_basic_block ( return_is_undefined_block ) ;
2023-07-14 21:57:49 +01:00
// 1. If generatorKind is async, set received.[[Value]] to ? Await(received.[[Value]]).
2024-02-04 08:00:54 +01:00
if ( generator . is_in_async_generator_function ( ) ) {
2025-03-31 09:32:39 +01:00
generate_await ( generator , received_completion_value , received_completion , received_completion_type , received_completion_value ) ;
2024-02-04 08:00:54 +01:00
}
2023-07-14 21:57:49 +01:00
2022-12-09 18:48:57 +00:00
// 2. Return ? received.
// NOTE: This will always be a return completion.
2024-05-12 11:03:26 +02:00
generator . emit_return < Bytecode : : Op : : Yield > ( received_completion_value ) ;
2022-12-09 18:48:57 +00:00
generator . switch_to_basic_block ( return_is_defined_block ) ;
// iv. Let innerReturnResult be ? Call(return, iterator, « received.[[Value]] »).
2024-05-07 21:36:56 +02:00
auto inner_return_result = generator . allocate_register ( ) ;
2025-12-28 17:48:01 +01:00
generator . emit_with_extra_operand_slots < Bytecode : : Op : : Call > ( 1 , inner_return_result , return_method , iterator , OptionalNone { } , ReadonlySpan < ScopedOperand > { & received_completion_value , 1 } ) ;
2022-12-09 18:48:57 +00:00
2023-07-14 21:57:49 +01:00
// v. If generatorKind is async, set innerReturnResult to ? Await(innerReturnResult).
2024-02-04 08:00:54 +01:00
if ( generator . is_in_async_generator_function ( ) ) {
2025-03-31 09:32:39 +01:00
auto new_value = generate_await ( generator , inner_return_result , received_completion , received_completion_type , received_completion_value ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( inner_return_result , new_value ) ;
2024-02-04 08:00:54 +01:00
}
2022-12-09 18:48:57 +00:00
// vi. If innerReturnResult is not an Object, throw a TypeError exception.
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : ThrowIfNotObject > ( inner_return_result ) ;
2022-12-09 18:48:57 +00:00
// vii. Let done be ? IteratorComplete(innerReturnResult).
2024-02-04 08:00:54 +01:00
generator . emit_iterator_complete ( done , inner_return_result ) ;
2022-12-09 18:48:57 +00:00
// viii. If done is true, then
auto & type_is_return_done_block = generator . make_block ( ) ;
auto & type_is_return_not_done_block = generator . make_block ( ) ;
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
done ,
2022-12-09 18:48:57 +00:00
Bytecode : : Label { type_is_return_done_block } ,
Bytecode : : Label { type_is_return_not_done_block } ) ;
generator . switch_to_basic_block ( type_is_return_done_block ) ;
// 1. Let value be ? IteratorValue(innerReturnResult).
2024-05-07 21:36:56 +02:00
auto inner_return_result_value = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit_iterator_value ( inner_return_result_value , inner_return_result ) ;
2022-12-09 18:48:57 +00:00
// 2. Return Completion Record { [[Type]]: return, [[Value]]: value, [[Target]]: empty }.
2024-05-12 11:03:26 +02:00
generator . emit_return < Bytecode : : Op : : Yield > ( inner_return_result_value ) ;
2022-12-09 18:48:57 +00:00
generator . switch_to_basic_block ( type_is_return_not_done_block ) ;
2023-07-14 21:57:49 +01:00
// ix. If generatorKind is async, set received to Completion(AsyncGeneratorYield(? IteratorValue(innerReturnResult))).
2022-12-09 18:48:57 +00:00
// x. Else, set received to Completion(GeneratorYield(innerReturnResult)).
// FIXME: Yield currently only accepts a Value, not an object conforming to the IteratorResult interface, so we have to do an observable lookup of `value` here.
2023-07-14 21:57:49 +01:00
// This only matters for non-async generators.
2024-05-07 21:36:56 +02:00
auto received = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit_iterator_value ( received , inner_return_result ) ;
2022-12-09 18:48:57 +00:00
2025-03-31 09:32:39 +01:00
generate_yield ( generator , Bytecode : : Label { continuation_block } , received , received_completion , received_completion_type , received_completion_value , AwaitBeforeYield : : No ) ;
2022-12-09 18:48:57 +00:00
generator . switch_to_basic_block ( continuation_block ) ;
2024-05-06 22:38:43 +02:00
if ( is_in_finalizer )
2025-03-28 00:56:57 +01:00
generator . emit_mov ( Bytecode : : Operand ( Bytecode : : Register : : exception ( ) ) , Bytecode : : Operand ( * saved_exception ) ) ;
2024-05-06 22:38:43 +02:00
2025-03-28 00:56:57 +01:00
generator . emit_mov ( received_completion , generator . accumulator ( ) ) ;
2025-03-31 09:32:39 +01:00
get_received_completion_type_and_value ( generator , received_completion , received_completion_type , received_completion_value ) ;
2022-12-09 18:48:57 +00:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { loop_block } ) ;
generator . switch_to_basic_block ( loop_end_block ) ;
2024-02-04 08:00:54 +01:00
return return_value ;
2022-02-12 19:54:08 +03:30
}
2021-06-14 15:46:41 +04:30
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > argument ;
2021-06-11 01:38:30 +04:30
if ( m_argument )
2026-02-11 22:42:53 +01:00
argument = m_argument - > generate_bytecode ( generator ) . value ( ) ;
2022-11-25 23:14:27 +00:00
else
2024-02-04 08:00:54 +01:00
argument = generator . add_constant ( js_undefined ( ) ) ;
2021-06-11 01:38:30 +04:30
auto & continuation_block = generator . make_block ( ) ;
2024-05-06 22:38:43 +02:00
if ( is_in_finalizer ) {
saved_exception = generator . allocate_register ( ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( Bytecode : : Operand ( * saved_exception ) , Bytecode : : Operand ( Bytecode : : Register : : exception ( ) ) ) ;
2024-05-06 22:38:43 +02:00
}
2025-03-31 09:32:39 +01:00
generate_yield ( generator , Bytecode : : Label { continuation_block } , * argument , received_completion , received_completion_type , received_completion_value , AwaitBeforeYield : : Yes ) ;
2021-06-11 01:38:30 +04:30
generator . switch_to_basic_block ( continuation_block ) ;
2024-05-06 22:38:43 +02:00
if ( is_in_finalizer )
2025-03-28 00:56:57 +01:00
generator . emit_mov ( Bytecode : : Operand ( Bytecode : : Register : : exception ( ) ) , Bytecode : : Operand ( * saved_exception ) ) ;
2024-05-06 22:38:43 +02:00
2025-03-28 00:56:57 +01:00
generator . emit_mov ( received_completion , generator . accumulator ( ) ) ;
2024-05-07 21:36:56 +02:00
2025-03-31 09:32:39 +01:00
get_received_completion_type_and_value ( generator , received_completion , received_completion_type , received_completion_value ) ;
2022-11-25 23:14:08 +00:00
auto & normal_completion_continuation_block = generator . make_block ( ) ;
auto & throw_completion_continuation_block = generator . make_block ( ) ;
2024-05-07 21:36:56 +02:00
auto received_completion_type_is_normal = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : StrictlyEquals > (
received_completion_type_is_normal ,
received_completion_type ,
generator . add_constant ( Value ( to_underlying ( Completion : : Type : : Normal ) ) ) ) ;
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
received_completion_type_is_normal ,
2022-11-25 23:14:08 +00:00
Bytecode : : Label { normal_completion_continuation_block } ,
Bytecode : : Label { throw_completion_continuation_block } ) ;
auto & throw_value_block = generator . make_block ( ) ;
auto & return_value_block = generator . make_block ( ) ;
generator . switch_to_basic_block ( throw_completion_continuation_block ) ;
2024-05-07 21:36:56 +02:00
auto received_completion_type_is_throw = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : StrictlyEquals > (
received_completion_type_is_throw ,
received_completion_type ,
generator . add_constant ( Value ( to_underlying ( Completion : : Type : : Throw ) ) ) ) ;
2022-11-25 23:14:08 +00:00
// If type is not equal to "throw" or "normal", assume it's "return".
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
received_completion_type_is_throw ,
2022-11-25 23:14:08 +00:00
Bytecode : : Label { throw_value_block } ,
Bytecode : : Label { return_value_block } ) ;
generator . switch_to_basic_block ( throw_value_block ) ;
generator . perform_needed_unwinds < Bytecode : : Op : : Throw > ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Throw > ( received_completion_value ) ;
2022-11-25 23:14:08 +00:00
generator . switch_to_basic_block ( return_value_block ) ;
2024-05-12 11:03:26 +02:00
generator . emit_return < Bytecode : : Op : : Yield > ( received_completion_value ) ;
2022-11-25 23:14:08 +00:00
generator . switch_to_basic_block ( normal_completion_continuation_block ) ;
2024-02-04 08:00:54 +01:00
return received_completion_value ;
2021-06-05 15:53:36 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > IfStatement : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2021-06-06 13:26:50 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2021-06-09 06:49:58 +04:30
// test
// jump if_true (true) true (false) false
// true
// jump always (true) end
// false
// jump always (true) end
// end
2026-02-11 22:42:53 +01:00
auto predicate = m_predicate - > generate_bytecode ( generator ) . value ( ) ;
2021-06-09 06:49:58 +04:30
2024-05-14 10:57:06 +02:00
Optional < ScopedOperand > completion ;
if ( generator . must_propagate_completion ( ) ) {
completion = choose_dst ( generator , preferred_dst ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( * completion , generator . add_constant ( js_undefined ( ) ) ) ;
2024-05-14 10:57:06 +02:00
}
2024-02-04 08:00:54 +01:00
2026-02-11 22:42:53 +01:00
auto build_block = [ & ] ( auto node , Optional < BasicBlock & > end_block = { } ) - > Optional < ScopedOperand > {
2026-02-10 23:25:24 +01:00
Optional < Bytecode : : Generator : : CompletionRegisterScope > completion_scope ;
if ( completion . has_value ( ) )
completion_scope . emplace ( generator , * completion ) ;
2026-02-11 22:42:53 +01:00
auto value = node - > generate_bytecode ( generator , completion ) ;
2026-01-25 10:57:43 -08:00
if ( ! generator . is_current_block_terminated ( ) ) {
if ( generator . must_propagate_completion ( ) & & value . has_value ( ) )
generator . emit_mov ( * completion , * value ) ;
if ( end_block . has_value ( ) )
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { * end_block } ) ;
}
return Optional < ScopedOperand > { } ;
} ;
// OPTIMIZATION: if the predicate is always true/false, only build the consequent/alternate blocks, respectively.
if ( auto constant = generator . try_get_constant ( predicate ) ; constant . has_value ( ) ) {
if ( constant - > to_boolean_slow_case ( ) ) {
2026-02-11 22:42:53 +01:00
( void ) build_block ( m_consequent ) ;
2026-01-25 10:57:43 -08:00
} else if ( m_alternate ) {
2026-02-11 22:42:53 +01:00
( void ) build_block ( m_alternate ) ;
2026-01-25 10:57:43 -08:00
}
return completion ;
}
auto & true_block = generator . make_block ( ) ;
auto & false_block = generator . make_block ( ) ;
// NOTE: if there is no 'else' block the end block is the same as the false block
auto & end_block = m_alternate ? generator . make_block ( ) : false_block ;
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
predicate ,
2021-06-09 06:49:58 +04:30
Bytecode : : Label { true_block } ,
Bytecode : : Label { false_block } ) ;
generator . switch_to_basic_block ( true_block ) ;
2026-02-11 22:42:53 +01:00
( void ) build_block ( m_consequent , { end_block } ) ;
2021-06-09 06:49:58 +04:30
2024-02-04 08:00:54 +01:00
if ( m_alternate ) {
2024-10-26 15:18:47 +02:00
generator . switch_to_basic_block ( false_block ) ;
2026-02-11 22:42:53 +01:00
( void ) build_block ( m_alternate , { end_block } ) ;
2024-02-04 08:00:54 +01:00
}
2021-06-09 06:49:58 +04:30
2021-06-20 12:38:59 +02:00
generator . switch_to_basic_block ( end_block ) ;
2024-02-04 08:00:54 +01:00
2024-05-14 10:57:06 +02:00
return completion ;
2021-06-06 13:26:50 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ContinueStatement : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-06 13:33:02 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2024-01-24 13:32:02 -05:00
if ( ! m_target_label . has_value ( ) ) {
2022-11-25 16:35:39 +01:00
generator . generate_continue ( ) ;
2024-05-07 21:36:56 +02:00
return Optional < ScopedOperand > { } ;
2022-06-11 23:09:37 +01:00
}
2024-01-24 13:32:02 -05:00
generator . generate_continue ( m_target_label . value ( ) ) ;
2024-05-07 21:36:56 +02:00
return Optional < ScopedOperand > { } ;
2021-06-06 13:33:02 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > DebuggerStatement : : generate_bytecode ( Bytecode : : Generator & , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-07 20:05:50 +01:00
{
2024-05-07 21:36:56 +02:00
return Optional < ScopedOperand > { } ;
2021-06-07 20:05:50 +01:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ConditionalExpression : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2021-06-08 04:54:34 +01:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2026-01-25 10:57:43 -08:00
2026-02-11 22:42:53 +01:00
auto test = m_test - > generate_bytecode ( generator ) . value ( ) ;
2026-01-25 10:57:43 -08:00
// OPTIMIZATION: if the predicate is always true/false, only build the consequent/alternate blocks, respectively.
if ( auto constant = generator . try_get_constant ( test ) ; constant . has_value ( ) ) {
auto is_always_true = constant - > to_boolean_slow_case ( ) ;
if ( is_always_true )
2026-02-11 22:42:53 +01:00
return m_consequent - > generate_bytecode ( generator ) . value ( ) ;
return m_alternate - > generate_bytecode ( generator ) . value ( ) ;
2026-01-25 10:57:43 -08:00
}
2021-06-09 06:49:58 +04:30
// test
// jump if_true (true) true (false) false
// true
// jump always (true) end
// false
// jump always (true) end
// end
auto & true_block = generator . make_block ( ) ;
auto & false_block = generator . make_block ( ) ;
auto & end_block = generator . make_block ( ) ;
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
test ,
2021-06-09 06:49:58 +04:30
Bytecode : : Label { true_block } ,
Bytecode : : Label { false_block } ) ;
2021-06-08 04:54:34 +01:00
2024-02-04 08:00:54 +01:00
auto dst = choose_dst ( generator , preferred_dst ) ;
2021-06-09 06:49:58 +04:30
generator . switch_to_basic_block ( true_block ) ;
2026-02-11 22:42:53 +01:00
auto consequent = m_consequent - > generate_bytecode ( generator ) . value ( ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( dst , consequent ) ;
2024-02-04 08:00:54 +01:00
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { end_block } ) ;
2021-06-08 04:54:34 +01:00
2021-06-09 06:49:58 +04:30
generator . switch_to_basic_block ( false_block ) ;
2026-02-11 22:42:53 +01:00
auto alternate = m_alternate - > generate_bytecode ( generator ) . value ( ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( dst , alternate ) ;
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { end_block } ) ;
2021-06-08 04:54:34 +01:00
2021-06-09 06:49:58 +04:30
generator . switch_to_basic_block ( end_block ) ;
2024-02-04 08:00:54 +01:00
return dst ;
2021-06-08 04:54:34 +01:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > SequenceExpression : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-08 10:13:37 +01:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > last_value ;
2024-02-04 08:00:54 +01:00
for ( auto & expression : m_expressions ) {
2026-02-11 22:42:53 +01:00
last_value = expression - > generate_bytecode ( generator ) ;
2024-02-04 08:00:54 +01:00
}
2022-02-12 19:54:08 +03:30
2024-02-04 08:00:54 +01:00
return last_value ;
2021-06-08 10:13:37 +01:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > TemplateLiteral : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2021-06-08 19:14:01 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2024-02-04 08:00:54 +01:00
auto dst = choose_dst ( generator , preferred_dst ) ;
2021-06-08 19:14:01 +02:00
2026-01-22 17:23:19 -08:00
Vector segments ( m_expressions ) ;
segments . remove_all_matching ( [ & ] ( auto expr ) {
return expr - > is_string_literal ( ) & & static_cast < StringLiteral const & > ( * expr ) . value ( ) . is_empty ( ) ;
} ) ;
// OPTIMIZATION: Empty template literal (``) can be turned into empty string literal ("")
if ( segments . size ( ) = = 0 )
return generator . add_constant ( Value { GC : : Ref { generator . vm ( ) . empty_string ( ) } } ) ;
if ( segments . size ( ) = = 1 ) {
2026-02-11 22:42:53 +01:00
auto value = segments [ 0 ] - > generate_bytecode ( generator ) . value ( ) ;
2026-01-22 17:23:19 -08:00
// OPTIMIZATION: String literal template (`xyz`) can be returned directly
if ( value . operand ( ) . is_constant ( ) )
return value ;
// OPTIMIZATION: `${x}` can be turned into ToString(x) op
generator . emit < Bytecode : : Op : : ToString > ( dst , value ) ;
return dst ;
}
for ( size_t i = 0 ; i < segments . size ( ) ; i + + ) {
auto expr = segments [ i ] ;
2026-02-11 22:42:53 +01:00
auto value = expr - > generate_bytecode ( generator ) . value ( ) ;
2026-01-22 17:23:19 -08:00
2021-06-07 20:58:36 -07:00
if ( i = = 0 ) {
2026-01-22 17:23:19 -08:00
if ( expr - > is_string_literal ( ) ) {
generator . emit_mov ( dst , value ) ;
} else {
generator . emit < Bytecode : : Op : : ToString > ( dst , value ) ;
}
2021-06-07 20:58:36 -07:00
} else {
2025-12-10 09:17:05 -06:00
generator . emit < Bytecode : : Op : : ConcatString > ( dst , value ) ;
2021-06-07 20:58:36 -07:00
}
2021-06-08 19:14:01 +02:00
}
2021-06-09 21:11:04 +02:00
2024-02-04 08:00:54 +01:00
return dst ;
2021-06-08 19:14:01 +02:00
}
2021-06-09 11:40:38 +02:00
2025-01-16 17:18:19 +00:00
struct TagAndThisValue {
ScopedOperand tag ;
ScopedOperand this_value ;
} ;
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > TaggedTemplateLiteral : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2021-06-09 21:02:24 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2026-02-11 22:42:53 +01:00
auto [ tag , this_value ] = [ & ] ( ) - > TagAndThisValue {
2025-01-16 17:18:19 +00:00
if ( is < MemberExpression > ( * m_tag ) ) {
auto & member_expression = static_cast < MemberExpression const & > ( * m_tag ) ;
2026-02-11 22:42:53 +01:00
auto base_and_value = get_base_and_value_from_member_expression ( generator , member_expression ) ;
2025-01-16 17:18:19 +00:00
return TagAndThisValue { . tag = base_and_value . value , . this_value = base_and_value . base } ;
}
2026-02-08 18:20:26 +01:00
if ( is < Identifier > ( * m_tag ) ) {
auto & identifier = static_cast < Identifier const & > ( * m_tag ) ;
if ( identifier . is_local ( ) | | identifier . is_global ( ) ) {
// Keep the normal Identifier path so local/global tags preserve
// TDZ behavior; only non-local identifiers need with-aware
// callee/this extraction.
2026-02-11 22:42:53 +01:00
auto tag = m_tag - > generate_bytecode ( generator ) . value ( ) ;
2026-02-08 18:20:26 +01:00
return TagAndThisValue { . tag = tag , . this_value = generator . add_constant ( js_undefined ( ) ) } ;
}
auto tag = generator . allocate_register ( ) ;
auto this_value = generator . allocate_register ( ) ;
generator . emit < Bytecode : : Op : : GetCalleeAndThisFromEnvironment > (
tag ,
this_value ,
generator . intern_identifier ( identifier . string ( ) ) ) ;
return TagAndThisValue { . tag = tag , . this_value = this_value } ;
}
2026-02-11 22:42:53 +01:00
auto tag = m_tag - > generate_bytecode ( generator ) . value ( ) ;
2025-01-16 17:18:19 +00:00
return TagAndThisValue { . tag = tag , . this_value = generator . add_constant ( js_undefined ( ) ) } ;
2026-02-11 22:42:53 +01:00
} ( ) ;
2021-06-09 22:07:18 +02:00
2026-01-06 19:49:38 +00:00
// 13.2.8.4 GetTemplateObject ( templateLiteral ), https://tc39.es/ecma262/#sec-gettemplateobject
2024-05-07 21:36:56 +02:00
Vector < ScopedOperand > string_regs ;
2021-06-09 21:02:24 +02:00
auto & expressions = m_template_literal - > expressions ( ) ;
2022-03-14 14:48:42 +00:00
2026-01-06 19:49:38 +00:00
for ( size_t i = 0 ; i < expressions . size ( ) ; i + = 2 ) {
2023-06-25 12:31:49 +02:00
// NOTE: If the string contains invalid escapes we get a null expression here,
// which we then convert to the expected `undefined` TV. See
// 12.9.6.1 Static Semantics: TV, https://tc39.es/ecma262/#sec-static-semantics-tv
2024-02-04 08:00:54 +01:00
if ( is < NullLiteral > ( expressions [ i ] ) ) {
2026-01-06 19:49:38 +00:00
string_regs . append ( generator . add_constant ( js_undefined ( ) ) ) ;
2024-02-04 08:00:54 +01:00
} else {
2026-02-11 22:42:53 +01:00
auto value = expressions [ i ] - > generate_bytecode ( generator ) . value ( ) ;
2026-01-06 19:49:38 +00:00
string_regs . append ( move ( value ) ) ;
2024-02-04 08:00:54 +01:00
}
2021-06-09 21:02:24 +02:00
}
2026-01-06 19:49:38 +00:00
auto & raw_strings = m_template_literal - > raw_strings ( ) ;
for ( auto const & raw_string : raw_strings ) {
2026-02-11 22:42:53 +01:00
auto value = raw_string - > generate_bytecode ( generator ) . value ( ) ;
2026-01-06 19:49:38 +00:00
string_regs . append ( move ( value ) ) ;
2022-03-14 14:48:42 +00:00
}
2021-06-09 21:02:24 +02:00
2026-01-06 19:49:38 +00:00
auto strings_array = generator . allocate_register ( ) ;
generator . emit_with_extra_operand_slots < Bytecode : : Op : : GetTemplateObject > (
string_regs . size ( ) ,
strings_array ,
generator . next_template_object_cache ( ) ,
string_regs ) ;
2024-05-07 21:36:56 +02:00
Vector < ScopedOperand > argument_regs ;
2024-02-04 08:00:54 +01:00
argument_regs . append ( strings_array ) ;
2021-06-09 21:02:24 +02:00
2022-09-07 23:39:43 +02:00
for ( size_t i = 1 ; i < expressions . size ( ) ; i + = 2 ) {
2026-02-11 22:42:53 +01:00
auto argument = expressions [ i ] - > generate_bytecode ( generator ) . value ( ) ;
2026-01-06 19:49:38 +00:00
argument_regs . append ( move ( argument ) ) ;
2021-06-09 21:02:24 +02:00
}
2024-02-04 08:00:54 +01:00
auto dst = choose_dst ( generator , preferred_dst ) ;
2026-01-06 19:49:38 +00:00
generator . emit_with_extra_operand_slots < Bytecode : : Op : : Call > ( argument_regs . size ( ) , dst , tag , this_value , OptionalNone { } , argument_regs ) ;
2024-02-04 08:00:54 +01:00
return dst ;
2021-06-09 21:02:24 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > UpdateExpression : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > ) const
2021-06-09 11:40:38 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2026-02-11 22:42:53 +01:00
auto reference = generator . emit_load_from_reference ( * m_argument ) ;
2021-06-09 11:40:38 +02:00
2026-03-01 13:17:26 +01:00
if ( ! reference . loaded_value . has_value ( ) )
return { } ;
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > previous_value_for_postfix ;
2021-06-09 11:40:38 +02:00
2024-02-20 11:45:01 +01:00
if ( m_op = = UpdateOp : : Increment ) {
if ( m_prefixed ) {
generator . emit < Bytecode : : Op : : Increment > ( * reference . loaded_value ) ;
} else {
2024-05-07 21:36:56 +02:00
previous_value_for_postfix = generator . allocate_register ( ) ;
2024-02-20 11:45:01 +01:00
generator . emit < Bytecode : : Op : : PostfixIncrement > ( * previous_value_for_postfix , * reference . loaded_value ) ;
}
} else {
if ( m_prefixed ) {
generator . emit < Bytecode : : Op : : Decrement > ( * reference . loaded_value ) ;
} else {
2024-05-07 21:36:56 +02:00
previous_value_for_postfix = generator . allocate_register ( ) ;
2024-02-20 11:45:01 +01:00
generator . emit < Bytecode : : Op : : PostfixDecrement > ( * previous_value_for_postfix , * reference . loaded_value ) ;
}
}
2021-06-09 11:40:38 +02:00
2024-02-04 08:00:54 +01:00
if ( is < Identifier > ( * m_argument ) )
2026-02-11 22:42:53 +01:00
generator . emit_store_to_reference ( static_cast < Identifier const & > ( * m_argument ) , * reference . loaded_value ) ;
2023-10-04 15:22:07 +02:00
else
2026-02-11 22:42:53 +01:00
generator . emit_store_to_reference ( reference , * reference . loaded_value ) ;
2021-06-09 11:40:38 +02:00
2021-10-25 15:17:41 +02:00
if ( ! m_prefixed )
2024-02-04 08:00:54 +01:00
return * previous_value_for_postfix ;
return * reference . loaded_value ;
2021-06-09 11:40:38 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ThrowStatement : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-09 18:18:56 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2026-02-11 22:42:53 +01:00
auto argument = m_argument - > generate_bytecode ( generator ) . value ( ) ;
2022-03-14 02:26:39 +00:00
generator . perform_needed_unwinds < Bytecode : : Op : : Throw > ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Throw > ( argument ) ;
2024-05-07 21:36:56 +02:00
return Optional < ScopedOperand > { } ;
2021-06-09 18:18:56 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > BreakStatement : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-10 20:28:43 +08:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2022-11-13 20:56:53 +01:00
// FIXME: Handle finally blocks in a graceful manner
// We need to execute the finally block, but tell it to resume
// execution at the designated block
2024-01-24 13:32:02 -05:00
if ( ! m_target_label . has_value ( ) ) {
2022-11-25 16:15:34 +01:00
generator . generate_break ( ) ;
2024-05-07 21:36:56 +02:00
return Optional < ScopedOperand > { } ;
2022-06-11 23:09:37 +01:00
}
2024-01-24 13:32:02 -05:00
generator . generate_break ( m_target_label . value ( ) ) ;
2024-05-07 21:36:56 +02:00
return Optional < ScopedOperand > { } ;
2021-06-10 20:28:43 +08:00
}
2026-02-09 01:41:19 +01:00
// Try/finally uses an explicit completion record protocol:
//
// 1. Allocate two registers: completion_type and completion_value
// 2. Every path into the finally body sets these before jumping:
// - Normal exit: completion_type = NORMAL
// - Exception: completion_type = THROW, completion_value = exception
// - Return: completion_type = RETURN, completion_value = return value
// - Break/continue: completion_type = FIRST_JUMP_INDEX + n
// 3. After the finally body, a dispatch chain checks completion_type
// and routes to the correct continuation (next block, jump target,
// return, or rethrow).
//
// For exceptions, the handler table points to an "exception preamble" block
// that catches the exception into completion_value, sets completion_type to
// THROW, and jumps to the finally body.
//
// For nested finally (e.g. break through two finally blocks), trampoline
// blocks chain through each finally layer, with each inner finally dispatching
// to a trampoline that sets up the outer finally's completion record.
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > TryStatement : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-10 15:04:38 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2021-06-10 15:04:38 +02:00
auto & saved_block = generator . current_block ( ) ;
Optional < Bytecode : : Label > handler_target ;
2023-10-19 23:18:54 +02:00
Optional < Bytecode : : Generator : : UnwindContext > unwind_context ;
2021-06-10 15:04:38 +02:00
Bytecode : : BasicBlock * next_block { nullptr } ;
2024-05-14 10:57:06 +02:00
Optional < ScopedOperand > completion ;
2024-02-04 08:00:54 +01:00
2026-02-09 01:41:19 +01:00
Optional < Bytecode : : Generator : : FinallyContext > finally_context ;
Bytecode : : BasicBlock * finally_body_block_ptr { nullptr } ;
2026-02-09 03:34:42 +01:00
// Capture the lexical environment at try entry for restoration on catch/exception.
Optional < ScopedOperand > lexical_environment_at_entry ;
lexical_environment_at_entry = generator . current_lexical_environment_register ( ) ;
2021-06-10 15:04:38 +02:00
if ( m_finalizer ) {
2026-02-09 01:41:19 +01:00
// Allocate completion record registers.
auto completion_type = generator . allocate_register ( ) ;
auto completion_value = generator . allocate_register ( ) ;
// Create the exception preamble block (handler table points here for exceptions).
auto & exception_preamble_block = generator . make_block ( ) ;
// Create the finally body block (all paths converge here).
auto & finally_body_block = generator . make_block ( ) ;
finally_body_block_ptr = & finally_body_block ;
// Set up FinallyContext.
finally_context . emplace ( Bytecode : : Generator : : FinallyContext {
. completion_type = completion_type ,
. completion_value = completion_value ,
. finally_body = Bytecode : : Label { finally_body_block } ,
. exception_preamble = Bytecode : : Label { exception_preamble_block } ,
. parent = generator . current_finally_context ( ) ,
. registered_jumps = { } ,
. next_jump_index = Bytecode : : Generator : : FinallyContext : : FIRST_JUMP_INDEX ,
2026-02-09 03:34:42 +01:00
. lexical_environment_at_entry = lexical_environment_at_entry ,
2026-02-09 01:41:19 +01:00
} ) ;
generator . set_current_finally_context ( & * finally_context ) ;
// Generate exception preamble:
// Catch completion_value
2026-02-09 03:34:42 +01:00
// SetLexicalEnvironment (restore to try entry)
2026-02-09 01:41:19 +01:00
// Mov completion_type, 1 (Throw)
// Jump finally_body
generator . switch_to_basic_block ( exception_preamble_block ) ;
generator . emit < Bytecode : : Op : : Catch > ( completion_value ) ;
2026-02-09 03:34:42 +01:00
generator . emit < Bytecode : : Op : : SetLexicalEnvironment > ( * lexical_environment_at_entry ) ;
2026-02-09 01:41:19 +01:00
generator . emit_mov ( completion_type , generator . add_constant ( Value ( Bytecode : : Generator : : FinallyContext : : THROW ) ) ) ;
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { finally_body_block } ) ;
2024-04-11 11:58:18 +02:00
2026-02-09 01:41:19 +01:00
// Set up unwind context with exception_preamble as finalizer.
2022-11-13 20:56:53 +01:00
generator . start_boundary ( Bytecode : : Generator : : BlockBoundaryType : : ReturnToFinally ) ;
2026-02-09 01:41:19 +01:00
unwind_context . emplace ( generator , Bytecode : : Label { exception_preamble_block } ) ;
2023-10-19 23:18:54 +02:00
}
2026-02-09 01:41:19 +01:00
2021-06-10 15:04:38 +02:00
if ( m_handler ) {
auto & handler_block = generator . make_block ( ) ;
generator . switch_to_basic_block ( handler_block ) ;
2022-11-13 20:56:53 +01:00
2024-05-07 21:36:56 +02:00
auto caught_value = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Catch > ( caught_value ) ;
2026-02-09 03:34:42 +01:00
generator . emit < Bytecode : : Op : : SetLexicalEnvironment > ( * lexical_environment_at_entry ) ;
2023-11-11 23:19:46 +01:00
2023-09-25 14:11:27 +02:00
// OPTIMIZATION: We avoid creating a lexical environment if the catch clause has no parameter.
bool did_create_variable_scope_for_catch_clause = false ;
2026-02-11 22:42:53 +01:00
m_handler - > parameter ( ) . visit (
[ & ] ( NonnullRefPtr < Identifier const > const & parameter ) - > void {
2025-04-22 00:05:36 +02:00
if ( parameter - > is_local ( ) ) {
2025-04-25 17:10:47 +02:00
auto local = generator . local ( parameter - > local_index ( ) ) ;
2025-04-22 00:05:36 +02:00
generator . emit_mov ( local , caught_value ) ;
2025-04-29 16:41:31 +02:00
generator . set_local_initialized ( parameter - > local_index ( ) ) ;
2025-04-22 00:05:36 +02:00
} else {
generator . begin_variable_scope ( ) ;
did_create_variable_scope_for_catch_clause = true ;
auto parameter_identifier = generator . intern_identifier ( parameter - > string ( ) ) ;
2025-11-20 22:14:50 +01:00
generator . emit < Bytecode : : Op : : CreateVariable > ( parameter_identifier , Bytecode : : Op : : EnvironmentMode : : Lexical , false , false , false ) ;
2025-04-22 00:05:36 +02:00
generator . emit < Bytecode : : Op : : InitializeLexicalBinding > ( parameter_identifier , caught_value ) ;
}
2021-07-11 15:15:38 +04:30
} ,
2026-02-11 22:42:53 +01:00
[ & ] ( NonnullRefPtr < BindingPattern const > const & binding_pattern ) - > void {
2025-04-22 00:05:36 +02:00
MUST ( binding_pattern - > for_each_bound_identifier ( [ & ] ( auto const & identifier ) {
if ( ! identifier . is_local ( ) )
did_create_variable_scope_for_catch_clause = true ;
} ) ) ;
if ( did_create_variable_scope_for_catch_clause )
generator . begin_variable_scope ( ) ;
MUST ( binding_pattern - > for_each_bound_identifier ( [ & ] ( auto const & identifier ) {
if ( identifier . is_local ( ) )
return ;
auto parameter_identifier = generator . intern_identifier ( identifier . string ( ) ) ;
2025-11-20 22:14:50 +01:00
generator . emit < Bytecode : : Op : : CreateVariable > ( parameter_identifier , Bytecode : : Op : : EnvironmentMode : : Lexical , false , false , false ) ;
2025-04-22 00:05:36 +02:00
} ) ) ;
2026-02-11 22:42:53 +01:00
binding_pattern - > generate_bytecode ( generator , Bytecode : : Op : : BindingInitializationMode : : Initialize , caught_value ) ;
2025-04-21 19:49:38 +02:00
} ,
2026-02-11 22:42:53 +01:00
[ ] ( Empty ) - > void {
} ) ;
2021-07-11 15:15:38 +04:30
2026-02-11 00:07:26 +01:00
Optional < ScopedOperand > catch_completion ;
{
// NB: The catch body needs its own completion register so that
// break/continue inside the catch block carries the catch's
// own completion value rather than leaking a value from an
// enclosing statement.
Optional < Bytecode : : Generator : : CompletionRegisterScope > completion_scope ;
if ( generator . must_propagate_completion ( ) ) {
catch_completion = generator . allocate_register ( ) ;
generator . emit_mov ( * catch_completion , generator . add_constant ( js_undefined ( ) ) ) ;
completion_scope . emplace ( generator , * catch_completion ) ;
}
2026-02-11 22:42:53 +01:00
( void ) m_handler - > body ( ) . generate_bytecode ( generator ) ;
2026-02-11 00:07:26 +01:00
}
2024-05-14 10:57:06 +02:00
if ( generator . must_propagate_completion ( ) ) {
2026-02-11 00:07:26 +01:00
if ( catch_completion . has_value ( ) & & ! generator . is_current_block_terminated ( ) ) {
2024-05-14 10:57:06 +02:00
completion = generator . allocate_register ( ) ;
2026-02-11 00:07:26 +01:00
generator . emit_mov ( * completion , * catch_completion ) ;
2024-05-14 10:57:06 +02:00
}
2024-02-04 08:00:54 +01:00
}
2021-06-10 15:04:38 +02:00
handler_target = Bytecode : : Label { handler_block } ;
2023-09-25 14:11:27 +02:00
if ( did_create_variable_scope_for_catch_clause )
generator . end_variable_scope ( ) ;
2022-03-14 02:38:13 +00:00
2021-06-10 15:04:38 +02:00
if ( ! generator . is_current_block_terminated ( ) ) {
if ( m_finalizer ) {
2026-02-09 01:41:19 +01:00
// Normal exit from catch → set completion_type=Normal, jump to finally.
generator . emit_mov ( finally_context - > completion_type , generator . add_constant ( Value ( Bytecode : : Generator : : FinallyContext : : NORMAL ) ) ) ;
generator . emit < Bytecode : : Op : : Jump > ( finally_context - > finally_body ) ;
2021-06-10 15:04:38 +02:00
} else {
VERIFY ( ! next_block ) ;
2023-10-19 23:18:54 +02:00
VERIFY ( ! unwind_context . has_value ( ) ) ;
2021-06-10 15:04:38 +02:00
next_block = & generator . make_block ( ) ;
2026-02-09 01:41:19 +01:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { * next_block } ) ;
2021-06-10 15:04:38 +02:00
}
}
}
2026-02-09 01:41:19 +01:00
2022-11-13 20:56:53 +01:00
if ( m_finalizer )
generator . end_boundary ( Bytecode : : Generator : : BlockBoundaryType : : ReturnToFinally ) ;
2023-10-19 23:18:54 +02:00
if ( m_handler ) {
2024-04-11 00:25:54 +02:00
if ( ! m_finalizer ) {
auto const * parent_unwind_context = generator . current_unwind_context ( ) ;
if ( parent_unwind_context )
2026-02-09 12:08:49 +01:00
unwind_context . emplace ( generator , parent_unwind_context - > handler ( ) ) ;
2024-04-11 00:25:54 +02:00
else
unwind_context . emplace ( generator , OptionalNone ( ) ) ;
}
2023-10-19 23:18:54 +02:00
unwind_context - > set_handler ( handler_target . value ( ) ) ;
}
2021-06-10 15:04:38 +02:00
2021-06-13 20:39:40 +04:30
auto & target_block = generator . make_block ( ) ;
2021-06-10 15:04:38 +02:00
generator . switch_to_basic_block ( saved_block ) ;
2026-02-09 11:19:53 +01:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { target_block } ) ;
2022-11-13 20:56:53 +01:00
if ( m_finalizer )
generator . start_boundary ( Bytecode : : Generator : : BlockBoundaryType : : ReturnToFinally ) ;
2021-06-13 20:39:40 +04:30
generator . switch_to_basic_block ( target_block ) ;
2026-02-11 00:07:26 +01:00
Optional < ScopedOperand > try_completion ;
{
// NB: The try body needs its own completion register so that
// break/continue inside the try block carries the try's own
// completion value rather than leaking a value from an enclosing
// statement.
Optional < Bytecode : : Generator : : CompletionRegisterScope > completion_scope ;
if ( generator . must_propagate_completion ( ) ) {
try_completion = generator . allocate_register ( ) ;
generator . emit_mov ( * try_completion , generator . add_constant ( js_undefined ( ) ) ) ;
completion_scope . emplace ( generator , * try_completion ) ;
}
2026-02-11 22:42:53 +01:00
( void ) m_block - > generate_bytecode ( generator ) ;
2026-02-11 00:07:26 +01:00
}
2021-11-10 22:22:26 +03:30
if ( ! generator . is_current_block_terminated ( ) ) {
2024-05-14 10:57:06 +02:00
if ( generator . must_propagate_completion ( ) ) {
2026-02-11 00:07:26 +01:00
if ( try_completion . has_value ( ) ) {
2024-05-14 10:57:06 +02:00
completion = generator . allocate_register ( ) ;
2026-02-11 00:07:26 +01:00
generator . emit_mov ( * completion , * try_completion ) ;
2024-05-14 10:57:06 +02:00
}
2024-02-04 08:00:54 +01:00
}
2021-11-10 22:22:26 +03:30
if ( m_finalizer ) {
2026-02-09 11:21:58 +01:00
// Normal exit from try → set completion_type=Normal, jump to finally.
2026-02-09 01:41:19 +01:00
generator . emit_mov ( finally_context - > completion_type , generator . add_constant ( Value ( Bytecode : : Generator : : FinallyContext : : NORMAL ) ) ) ;
generator . emit < Bytecode : : Op : : Jump > ( finally_context - > finally_body ) ;
2021-11-10 22:22:26 +03:30
} else {
2023-10-19 23:18:54 +02:00
VERIFY ( unwind_context . has_value ( ) ) ;
unwind_context . clear ( ) ;
2023-05-13 18:50:27 +02:00
if ( ! next_block )
next_block = & generator . make_block ( ) ;
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { * next_block } ) ;
2021-11-10 22:22:26 +03:30
}
}
2022-11-13 20:56:53 +01:00
if ( m_finalizer )
generator . end_boundary ( Bytecode : : Generator : : BlockBoundaryType : : ReturnToFinally ) ;
2021-06-10 15:04:38 +02:00
2026-02-09 01:41:19 +01:00
// Now generate the finally body and after-finally dispatch.
// We deferred this so that registered_jumps from break/continue in the try body are available.
if ( m_finalizer & & finally_context . has_value ( ) ) {
generator . set_current_finally_context ( finally_context - > parent ) ;
// Clear the unwind context so that blocks created during finally body generation
// don't inherit the inner handler/finalizer (the inner unwind context is already
// popped at runtime by the time the finally body runs).
unwind_context . clear ( ) ;
generator . switch_to_basic_block ( * finally_body_block_ptr ) ;
generator . start_boundary ( Bytecode : : Generator : : BlockBoundaryType : : LeaveFinally ) ;
2026-02-11 00:07:26 +01:00
{
// NB: The finally body needs its own completion register so that
// break/continue inside the finally block carries the finally's
// own completion value (initialized to undefined) rather than
// leaking the try/catch block's completion value through.
Optional < ScopedOperand > finally_completion ;
Optional < Bytecode : : Generator : : CompletionRegisterScope > completion_scope ;
if ( generator . must_propagate_completion ( ) ) {
finally_completion = generator . allocate_register ( ) ;
generator . emit_mov ( * finally_completion , generator . add_constant ( js_undefined ( ) ) ) ;
completion_scope . emplace ( generator , * finally_completion ) ;
}
2026-02-11 22:42:53 +01:00
( void ) m_finalizer - > generate_bytecode ( generator ) ;
2026-02-11 00:07:26 +01:00
}
2026-02-09 01:41:19 +01:00
generator . end_boundary ( Bytecode : : Generator : : BlockBoundaryType : : LeaveFinally ) ;
if ( ! generator . is_current_block_terminated ( ) ) {
if ( ! next_block )
next_block = & generator . make_block ( ) ;
auto const & completion_type = finally_context - > completion_type ;
auto const & completion_value = finally_context - > completion_value ;
// After-finally dispatch chain: a series of JumpStrictlyEquals that check
// completion_type and route to the right continuation. Order:
// 1. NORMAL → fall through to next block
// 2. Each registered break/continue target
// 3. RETURN → return/yield the completion_value
// 4. Default → rethrow completion_value (must be THROW)
auto & after_normal_check = generator . make_block ( ) ;
generator . emit < Bytecode : : Op : : JumpStrictlyEquals > (
completion_type , generator . add_constant ( Value ( Bytecode : : Generator : : FinallyContext : : NORMAL ) ) ,
Bytecode : : Label { * next_block } , Bytecode : : Label { after_normal_check } ) ;
generator . switch_to_basic_block ( after_normal_check ) ;
// Registered break/continue jumps (indices 3+)
for ( auto const & jump : finally_context - > registered_jumps ) {
auto & after_jump_check = generator . make_block ( ) ;
generator . emit < Bytecode : : Op : : JumpStrictlyEquals > (
completion_type , generator . add_constant ( Value ( jump . index ) ) ,
jump . target , Bytecode : : Label { after_jump_check } ) ;
generator . switch_to_basic_block ( after_jump_check ) ;
}
auto & return_block = generator . make_block ( ) ;
auto & rethrow_block = generator . make_block ( ) ;
generator . emit < Bytecode : : Op : : JumpStrictlyEquals > (
completion_type , generator . add_constant ( Value ( Bytecode : : Generator : : FinallyContext : : RETURN ) ) ,
Bytecode : : Label { return_block } , Bytecode : : Label { rethrow_block } ) ;
// Generate return block.
generator . switch_to_basic_block ( return_block ) ;
if ( finally_context - > parent ) {
// Nested finally: copy completion record to outer and jump to outer finally body.
auto & outer = * finally_context - > parent ;
generator . emit_mov ( outer . completion_type , completion_type ) ;
generator . emit_mov ( outer . completion_value , completion_value ) ;
generator . emit < Bytecode : : Op : : Jump > ( outer . finally_body ) ;
} else {
if ( generator . is_in_generator_function ( ) ) {
generator . emit < Bytecode : : Op : : Yield > ( OptionalNone { } , completion_value ) ;
} else {
generator . emit < Bytecode : : Op : : Return > ( completion_value ) ;
}
}
// Default: rethrow the exception.
generator . switch_to_basic_block ( rethrow_block ) ;
generator . emit < Bytecode : : Op : : Throw > ( completion_value ) ;
}
}
2021-06-10 15:04:38 +02:00
generator . switch_to_basic_block ( next_block ? * next_block : saved_block ) ;
2024-05-14 10:57:06 +02:00
if ( generator . must_propagate_completion ( ) ) {
if ( ! completion . has_value ( ) )
return generator . add_constant ( js_undefined ( ) ) ;
}
return completion ;
2021-06-10 15:04:38 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > SwitchStatement : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2022-06-11 23:09:37 +01:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2022-06-11 23:09:37 +01:00
return generate_labelled_evaluation ( generator , { } ) ;
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > SwitchStatement : : generate_labelled_evaluation ( Bytecode : : Generator & generator , Vector < FlyString > const & label_set , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-11 00:36:16 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2024-05-07 21:36:56 +02:00
2024-05-14 10:57:06 +02:00
Optional < ScopedOperand > completion ;
if ( generator . must_propagate_completion ( ) ) {
completion = generator . allocate_register ( ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( * completion , generator . add_constant ( js_undefined ( ) ) ) ;
2024-05-14 10:57:06 +02:00
}
2024-05-07 21:36:56 +02:00
2026-02-11 22:42:53 +01:00
auto discriminant = m_discriminant - > generate_bytecode ( generator ) . value ( ) ;
2021-06-11 00:36:16 +02:00
Vector < Bytecode : : BasicBlock & > case_blocks ;
2023-06-28 11:45:19 +02:00
Bytecode : : BasicBlock * entry_block_for_default { nullptr } ;
2021-06-11 00:36:16 +02:00
Bytecode : : BasicBlock * next_test_block = & generator . make_block ( ) ;
2022-04-03 02:13:51 +04:30
2024-05-11 13:25:08 +02:00
bool did_create_lexical_environment = false ;
if ( has_lexical_declarations ( ) )
did_create_lexical_environment = generator . emit_block_declaration_instantiation ( * this ) ;
2022-04-03 02:13:51 +04:30
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { * next_test_block } ) ;
2022-04-03 02:13:51 +04:30
2024-05-08 13:08:38 +02:00
Queue < Bytecode : : BasicBlock * > test_blocks ;
for ( auto & switch_case : m_cases ) {
if ( switch_case - > test ( ) )
test_blocks . enqueue ( & generator . make_block ( ) ) ;
}
2021-06-11 00:36:16 +02:00
for ( auto & switch_case : m_cases ) {
auto & case_block = generator . make_block ( ) ;
2023-03-06 14:17:01 +01:00
if ( switch_case - > test ( ) ) {
2021-06-11 00:36:16 +02:00
generator . switch_to_basic_block ( * next_test_block ) ;
2026-02-11 22:42:53 +01:00
auto test_value = switch_case - > test ( ) - > generate_bytecode ( generator ) . value ( ) ;
2024-05-07 21:36:56 +02:00
auto result = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : StrictlyEquals > ( result , test_value , discriminant ) ;
2024-05-08 13:08:38 +02:00
next_test_block = test_blocks . dequeue ( ) ;
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
result ,
2024-05-08 12:55:32 +02:00
Bytecode : : Label { case_block } ,
2023-09-28 12:43:11 +02:00
Bytecode : : Label { * next_test_block } ) ;
2021-06-11 00:36:16 +02:00
} else {
2024-05-08 12:55:32 +02:00
entry_block_for_default = & case_block ;
2021-06-11 00:36:16 +02:00
}
2023-06-28 11:45:19 +02:00
2021-06-11 00:36:16 +02:00
case_blocks . append ( case_block ) ;
}
generator . switch_to_basic_block ( * next_test_block ) ;
auto & end_block = generator . make_block ( ) ;
2023-06-28 11:45:19 +02:00
if ( entry_block_for_default ! = nullptr ) {
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { * entry_block_for_default } ) ;
2021-06-11 00:36:16 +02:00
} else {
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { end_block } ) ;
2021-06-11 00:36:16 +02:00
}
auto current_block = case_blocks . begin ( ) ;
2026-02-10 23:25:24 +01:00
generator . begin_breakable_scope ( Bytecode : : Label { end_block } , label_set , completion ) ;
2021-06-11 00:36:16 +02:00
for ( auto & switch_case : m_cases ) {
generator . switch_to_basic_block ( * current_block ) ;
2026-02-10 23:25:24 +01:00
{
Optional < Bytecode : : Generator : : CompletionRegisterScope > completion_scope ;
if ( completion . has_value ( ) )
completion_scope . emplace ( generator , * completion ) ;
for ( auto & statement : switch_case - > children ( ) ) {
2026-02-11 22:42:53 +01:00
auto result = statement - > generate_bytecode ( generator ) ;
2026-02-10 23:25:24 +01:00
if ( generator . is_current_block_terminated ( ) )
break ;
if ( generator . must_propagate_completion ( ) ) {
if ( result . has_value ( ) )
generator . emit_mov ( * completion , * result ) ;
}
2024-05-14 10:57:06 +02:00
}
2021-06-11 00:36:16 +02:00
}
if ( ! generator . is_current_block_terminated ( ) ) {
auto next_block = current_block ;
next_block + + ;
if ( next_block . is_end ( ) ) {
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { end_block } ) ;
2021-06-11 00:36:16 +02:00
} else {
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { * next_block } ) ;
2021-06-11 00:36:16 +02:00
}
}
current_block + + ;
}
generator . end_breakable_scope ( ) ;
generator . switch_to_basic_block ( end_block ) ;
2023-06-25 07:37:24 +02:00
2024-05-11 13:25:08 +02:00
if ( did_create_lexical_environment )
2023-06-25 07:37:24 +02:00
generator . end_variable_scope ( ) ;
2024-05-14 10:57:06 +02:00
return completion ;
2021-06-11 00:36:16 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > SuperExpression : : generate_bytecode ( Bytecode : : Generator & , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2023-07-06 17:16:20 -04:00
{
// The semantics for SuperExpression are handled in CallExpression and SuperCall.
VERIFY_NOT_REACHED ( ) ;
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ClassDeclaration : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2021-06-30 15:42:13 -03:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2026-02-11 22:42:53 +01:00
auto value = m_class_expression - > generate_bytecode ( generator ) . value ( ) ;
2024-05-14 11:30:30 +02:00
generator . emit_set_variable ( * m_class_expression . ptr ( ) - > m_name , value , Bytecode : : Op : : BindingInitializationMode : : Initialize ) ;
2024-02-04 08:00:54 +01:00
// NOTE: ClassDeclaration does not produce a value.
2024-05-07 21:36:56 +02:00
return Optional < ScopedOperand > { } ;
2022-02-12 19:55:40 +03:30
}
2023-06-28 18:22:27 +03:00
// 15.7.14 Runtime Semantics: ClassDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ClassExpression : : generate_bytecode_with_lhs_name ( Bytecode : : Generator & generator , Optional < Bytecode : : IdentifierTableIndex > lhs_name , Optional < ScopedOperand > preferred_dst ) const
2022-02-12 19:55:40 +03:30
{
2023-06-28 18:22:27 +03:00
// NOTE: Step 2 is not a part of NewClass instruction because it is assumed to be done before super class expression evaluation
2026-02-09 03:34:42 +01:00
auto parent_environment = generator . current_lexical_environment_register ( ) ;
auto class_environment = generator . allocate_register ( ) ;
generator . emit < Bytecode : : Op : : CreateLexicalEnvironment > ( class_environment , parent_environment , 0 ) ;
generator . push_lexical_environment_register ( class_environment ) ;
2023-06-28 18:22:27 +03:00
if ( has_name ( ) | | ! lhs_name . has_value ( ) ) {
// NOTE: Step 3.a is not a part of NewClass instruction because it is assumed to be done before super class expression evaluation
2023-06-30 16:51:39 +03:00
auto interned_index = generator . intern_identifier ( name ( ) ) ;
2025-11-20 22:14:50 +01:00
generator . emit < Bytecode : : Op : : CreateVariable > ( interned_index , Bytecode : : Op : : EnvironmentMode : : Lexical , true , false , false ) ;
2023-06-28 18:22:27 +03:00
}
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > super_class ;
2023-06-28 18:22:27 +03:00
if ( m_super_class )
2026-02-11 22:42:53 +01:00
super_class = m_super_class - > generate_bytecode ( generator ) . value ( ) ;
2023-06-28 18:22:27 +03:00
2025-07-30 12:10:17 +02:00
bool did_emit_private_environment_allocation = false ;
2024-05-11 22:54:41 +00:00
for ( auto const & element : m_elements ) {
auto opt_private_name = element - > private_bound_identifier ( ) ;
if ( opt_private_name . has_value ( ) ) {
2025-07-30 12:10:17 +02:00
if ( ! did_emit_private_environment_allocation ) {
generator . emit < Op : : CreatePrivateEnvironment > ( ) ;
did_emit_private_environment_allocation = true ;
}
2024-05-11 22:54:41 +00:00
generator . emit < Op : : AddPrivateName > ( generator . intern_identifier ( * opt_private_name ) ) ;
}
}
Vector < Optional < ScopedOperand > > elements ;
for ( auto const & element : m_elements ) {
Optional < ScopedOperand > key ;
if ( is < ClassMethod > ( * element ) ) {
auto const & class_method = static_cast < ClassMethod const & > ( * element ) ;
if ( ! is < PrivateIdentifier > ( class_method . key ( ) ) )
2026-02-11 22:42:53 +01:00
key = class_method . key ( ) . generate_bytecode ( generator ) ;
2024-05-11 22:54:41 +00:00
} else if ( is < ClassField > ( * element ) ) {
auto const & class_field = static_cast < ClassField const & > ( * element ) ;
if ( ! is < PrivateIdentifier > ( class_field . key ( ) ) )
2026-02-11 22:42:53 +01:00
key = class_field . key ( ) . generate_bytecode ( generator ) ;
2024-05-11 22:54:41 +00:00
}
elements . append ( { key } ) ;
}
2026-02-11 00:20:47 +01:00
// Build a ClassBlueprint that captures all class element metadata at codegen time.
auto & vm = generator . vm ( ) ;
ClassBlueprint blueprint ;
blueprint . has_super_class = ! m_super_class . is_null ( ) ;
blueprint . has_name = has_name ( ) ;
blueprint . name = name ( ) ;
blueprint . source_text = source_text ( ) ;
// Register shared function data for the constructor.
2026-02-11 11:51:48 +01:00
auto constructor_shared_data = SharedFunctionInstanceData : : create_for_function_node ( vm , * m_constructor ) ;
2026-02-11 00:20:47 +01:00
blueprint . constructor_shared_function_data_index = generator . register_shared_function_data ( constructor_shared_data ) ;
for ( auto const & element : m_elements ) {
if ( is < ClassMethod > ( * element ) ) {
auto const & class_method = static_cast < ClassMethod const & > ( * element ) ;
bool is_private = is < PrivateIdentifier > ( class_method . key ( ) ) ;
ClassElementDescriptor : : Kind descriptor_kind ;
switch ( class_method . kind ( ) ) {
case ClassMethod : : Kind : : Method :
descriptor_kind = ClassElementDescriptor : : Kind : : Method ;
break ;
case ClassMethod : : Kind : : Getter :
descriptor_kind = ClassElementDescriptor : : Kind : : Getter ;
break ;
case ClassMethod : : Kind : : Setter :
descriptor_kind = ClassElementDescriptor : : Kind : : Setter ;
break ;
}
2026-02-11 11:51:48 +01:00
auto shared_data = SharedFunctionInstanceData : : create_for_function_node ( vm , class_method . function ( ) ) ;
2026-02-11 00:20:47 +01:00
auto data_index = generator . register_shared_function_data ( shared_data ) ;
blueprint . elements . append ( {
. kind = descriptor_kind ,
. is_static = element - > is_static ( ) ,
. is_private = is_private ,
. private_identifier = is_private ? Optional < Utf16FlyString > ( static_cast < PrivateIdentifier const & > ( class_method . key ( ) ) . string ( ) ) : Optional < Utf16FlyString > ( ) ,
. shared_function_data_index = data_index ,
. has_initializer = false ,
. literal_value = { } ,
} ) ;
} else if ( is < ClassField > ( * element ) ) {
auto const & class_field = static_cast < ClassField const & > ( * element ) ;
bool is_private = is < PrivateIdentifier > ( class_field . key ( ) ) ;
Optional < u32 > data_index ;
bool has_initializer = class_field . initializer ( ) ! = nullptr ;
Optional < Value > literal_value ;
if ( has_initializer ) {
auto const & initializer = * class_field . initializer ( ) ;
// Detect literal initializers and store the value directly,
// avoiding function creation and calls for simple cases like x = 0.
if ( is < NumericLiteral > ( initializer ) ) {
literal_value = static_cast < NumericLiteral const & > ( initializer ) . value ( ) ;
} else if ( is < BooleanLiteral > ( initializer ) ) {
literal_value = static_cast < BooleanLiteral const & > ( initializer ) . value ( ) ;
} else if ( is < NullLiteral > ( initializer ) ) {
literal_value = js_null ( ) ;
} else if ( is < StringLiteral > ( initializer ) ) {
literal_value = Value ( PrimitiveString : : create ( vm , static_cast < StringLiteral const & > ( initializer ) . value ( ) ) ) ;
} else if ( is < UnaryExpression > ( initializer ) ) {
auto const & unary = static_cast < UnaryExpression const & > ( initializer ) ;
if ( unary . op ( ) = = UnaryOp : : Minus & & is < NumericLiteral > ( * unary . lhs ( ) ) )
literal_value = Value ( - static_cast < NumericLiteral const & > ( * unary . lhs ( ) ) . value ( ) . as_double ( ) ) ;
}
if ( ! literal_value . has_value ( ) ) {
// FIXME: For computed-key fields, the field name for anonymous function
// naming is only known at runtime. We use "" here, which means
// e.g. (new (class { [sym] = function(){} }))[sym].name would be
// "" instead of "[sym]". Non-computed keys are handled correctly.
Utf16FlyString field_name ;
if ( is_private ) {
field_name = static_cast < PrivateIdentifier const & > ( class_field . key ( ) ) . string ( ) ;
} else if ( is < Identifier > ( class_field . key ( ) ) ) {
field_name = static_cast < Identifier const & > ( class_field . key ( ) ) . string ( ) ;
} else if ( is < StringLiteral > ( class_field . key ( ) ) ) {
field_name = Utf16FlyString ( static_cast < StringLiteral const & > ( class_field . key ( ) ) . value ( ) ) ;
} else if ( is < NumericLiteral > ( class_field . key ( ) ) ) {
field_name = Utf16FlyString ( number_to_utf16_string ( static_cast < NumericLiteral const & > ( class_field . key ( ) ) . value ( ) . as_double ( ) ) ) ;
2026-02-11 19:45:44 +01:00
} else if ( is < BigIntLiteral > ( class_field . key ( ) ) ) {
field_name = Utf16FlyString : : from_utf8 ( bigint_literal_to_decimal_string ( static_cast < BigIntLiteral const & > ( class_field . key ( ) ) ) ) ;
2026-02-11 00:20:47 +01:00
}
auto copy_initializer = class_field . initializer ( ) ;
auto function_code = create_ast_node < ClassFieldInitializerStatement > (
class_field . initializer ( ) - > source_range ( ) ,
copy_initializer . release_nonnull ( ) ,
move ( field_name ) ) ;
FunctionParsingInsights parsing_insights ;
parsing_insights . uses_this_from_environment = true ;
parsing_insights . uses_this = true ;
auto shared_data = vm . heap ( ) . allocate < SharedFunctionInstanceData > (
vm ,
FunctionKind : : Normal ,
" field " _utf16_fly_string ,
0 ,
FunctionParameters : : empty ( ) ,
* function_code ,
Utf16View { } ,
true ,
false ,
parsing_insights ,
Vector < LocalVariable > { } ) ;
2026-02-11 00:28:10 +01:00
// Set class_field_initializer_name for keys known at codegen time.
// This is needed so eval("arguments") inside field initializers
// correctly throws a SyntaxError.
if ( is_private ) {
auto private_name = static_cast < PrivateIdentifier const & > ( class_field . key ( ) ) . string ( ) ;
shared_data - > m_class_field_initializer_name = PrivateName ( 0 , private_name ) ;
} else if ( is < Identifier > ( class_field . key ( ) ) ) {
auto name = static_cast < Identifier const & > ( class_field . key ( ) ) . string ( ) ;
shared_data - > m_class_field_initializer_name = PropertyKey ( name . to_utf16_string ( ) ) ;
} else if ( is < StringLiteral > ( class_field . key ( ) ) ) {
auto name = static_cast < StringLiteral const & > ( class_field . key ( ) ) . value ( ) ;
shared_data - > m_class_field_initializer_name = PropertyKey ( name ) ;
} else if ( is < NumericLiteral > ( class_field . key ( ) ) ) {
auto name = number_to_utf16_string ( static_cast < NumericLiteral const & > ( class_field . key ( ) ) . value ( ) . as_double ( ) ) ;
shared_data - > m_class_field_initializer_name = PropertyKey ( name ) ;
2026-02-11 19:45:44 +01:00
} else if ( is < BigIntLiteral > ( class_field . key ( ) ) ) {
auto name = bigint_literal_to_decimal_string ( static_cast < BigIntLiteral const & > ( class_field . key ( ) ) ) ;
shared_data - > m_class_field_initializer_name = PropertyKey ( Utf16String : : from_utf8 ( name ) ) ;
2026-02-11 00:28:10 +01:00
}
// For computed keys, class_field_initializer_name is set at runtime
// in construct_class().
2026-02-11 00:20:47 +01:00
data_index = generator . register_shared_function_data ( shared_data ) ;
}
}
blueprint . elements . append ( {
. kind = ClassElementDescriptor : : Kind : : Field ,
. is_static = element - > is_static ( ) ,
. is_private = is_private ,
. private_identifier = is_private ? Optional < Utf16FlyString > ( static_cast < PrivateIdentifier const & > ( class_field . key ( ) ) . string ( ) ) : Optional < Utf16FlyString > ( ) ,
. shared_function_data_index = data_index ,
. has_initializer = has_initializer ,
. literal_value = literal_value ,
} ) ;
} else if ( is < StaticInitializer > ( * element ) ) {
auto const & static_init = static_cast < StaticInitializer const & > ( * element ) ;
FunctionParsingInsights parsing_insights ;
parsing_insights . uses_this_from_environment = true ;
parsing_insights . uses_this = true ;
auto shared_data = vm . heap ( ) . allocate < SharedFunctionInstanceData > (
vm ,
FunctionKind : : Normal ,
Utf16FlyString { } ,
0 ,
FunctionParameters : : empty ( ) ,
static_init . function_body ( ) ,
Utf16View { } ,
true ,
false ,
parsing_insights ,
static_init . function_body ( ) . local_variables_names ( ) ) ;
auto data_index = generator . register_shared_function_data ( shared_data ) ;
blueprint . elements . append ( {
. kind = ClassElementDescriptor : : Kind : : StaticInitializer ,
. is_static = true ,
. is_private = false ,
. private_identifier = { } ,
. shared_function_data_index = data_index ,
. has_initializer = false ,
. literal_value = { } ,
} ) ;
}
}
auto blueprint_index = generator . register_class_blueprint ( move ( blueprint ) ) ;
2026-02-09 03:34:42 +01:00
// Restore parent environment before emitting NewClass.
generator . emit < Bytecode : : Op : : SetLexicalEnvironment > ( parent_environment ) ;
generator . pop_lexical_environment_register ( ) ;
2024-02-04 08:00:54 +01:00
auto dst = choose_dst ( generator , preferred_dst ) ;
2026-02-11 00:28:10 +01:00
generator . emit_with_extra_slots < Op : : NewClass , Optional < Operand > > ( elements . size ( ) , dst , super_class . has_value ( ) ? super_class - > operand ( ) : Optional < Operand > { } , class_environment , blueprint_index , lhs_name , elements ) ;
2024-05-11 22:54:41 +00:00
2025-07-30 12:10:17 +02:00
if ( did_emit_private_environment_allocation ) {
generator . emit < Op : : LeavePrivateEnvironment > ( ) ;
}
2023-06-28 18:22:27 +03:00
2024-02-04 08:00:54 +01:00
return dst ;
2021-06-30 15:42:13 -03:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ClassExpression : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2023-06-23 14:27:42 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2024-02-04 08:00:54 +01:00
return generate_bytecode_with_lhs_name ( generator , { } , preferred_dst ) ;
2023-06-23 14:27:42 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > SpreadExpression : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2022-09-02 23:39:04 +02:00
{
// NOTE: All users of this should handle the behaviour of this on their own,
// assuming it returns an Array-like object
return m_target - > generate_bytecode ( generator ) ;
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ThisExpression : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2021-10-24 14:43:00 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2024-05-07 12:16:37 +02:00
return generator . get_this ( preferred_dst ) ;
2021-10-24 14:43:00 +02:00
}
2024-05-07 21:36:56 +02:00
static ScopedOperand generate_await (
2024-02-04 08:00:54 +01:00
Bytecode : : Generator & generator ,
2024-05-07 21:36:56 +02:00
ScopedOperand argument ,
ScopedOperand received_completion ,
ScopedOperand received_completion_type ,
2025-03-31 09:32:39 +01:00
ScopedOperand received_completion_value )
2021-11-11 00:46:07 +03:30
{
VERIFY ( generator . is_in_async_function ( ) ) ;
auto & continuation_block = generator . make_block ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Await > ( Bytecode : : Label { continuation_block } , argument ) ;
2021-11-11 00:46:07 +03:30
generator . switch_to_basic_block ( continuation_block ) ;
2022-12-25 17:15:29 +01:00
2024-02-04 08:00:54 +01:00
// FIXME: It's really magical that we can just assume that the completion value is in register 0.
// It ends up there because we "return" from the Await instruction above via the synthetic
// generator function that actually drives async execution.
2025-03-28 00:56:57 +01:00
generator . emit_mov ( received_completion , generator . accumulator ( ) ) ;
2025-03-31 09:32:39 +01:00
get_received_completion_type_and_value ( generator , received_completion , received_completion_type , received_completion_value ) ;
2022-12-25 17:15:29 +01:00
auto & normal_completion_continuation_block = generator . make_block ( ) ;
auto & throw_value_block = generator . make_block ( ) ;
2024-05-07 21:36:56 +02:00
auto received_completion_type_is_normal = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : StrictlyEquals > (
received_completion_type_is_normal ,
received_completion_type ,
generator . add_constant ( Value ( to_underlying ( Completion : : Type : : Normal ) ) ) ) ;
2024-05-09 15:13:31 +02:00
generator . emit_jump_if (
2024-02-04 08:00:54 +01:00
received_completion_type_is_normal ,
2022-12-25 17:15:29 +01:00
Bytecode : : Label { normal_completion_continuation_block } ,
Bytecode : : Label { throw_value_block } ) ;
2023-07-14 21:57:49 +01:00
// Simplification: The only abrupt completion we receive from AsyncFunctionDriverWrapper or AsyncGenerator is Type::Throw
2022-12-25 17:15:29 +01:00
// So we do not need to account for the Type::Return path
generator . switch_to_basic_block ( throw_value_block ) ;
generator . perform_needed_unwinds < Bytecode : : Op : : Throw > ( ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : Throw > ( received_completion_value ) ;
2022-12-25 17:15:29 +01:00
generator . switch_to_basic_block ( normal_completion_continuation_block ) ;
2024-02-04 08:00:54 +01:00
return received_completion_value ;
2023-06-27 19:24:34 +01:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > AwaitExpression : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2023-06-27 19:24:34 +01:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2026-02-11 22:42:53 +01:00
auto argument = m_argument - > generate_bytecode ( generator ) . value ( ) ;
2024-02-04 08:00:54 +01:00
2024-05-07 21:36:56 +02:00
auto received_completion = generator . allocate_register ( ) ;
auto received_completion_type = generator . allocate_register ( ) ;
auto received_completion_value = generator . allocate_register ( ) ;
2023-07-14 21:57:49 +01:00
2025-03-28 00:56:57 +01:00
generator . emit_mov ( received_completion , generator . accumulator ( ) ) ;
2023-07-14 21:57:49 +01:00
2025-03-31 09:32:39 +01:00
return generate_await ( generator , argument , received_completion , received_completion_type , received_completion_value ) ;
2021-11-11 00:46:07 +03:30
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > WithStatement : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2022-03-13 16:01:18 +03:30
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2026-02-11 22:42:53 +01:00
auto object = m_object - > generate_bytecode ( generator ) . value ( ) ;
2026-02-09 03:34:42 +01:00
auto object_environment = generator . allocate_register ( ) ;
generator . emit < Bytecode : : Op : : EnterObjectEnvironment > ( object_environment , object ) ;
generator . push_lexical_environment_register ( object_environment ) ;
2022-03-14 02:35:45 +00:00
// EnterObjectEnvironment sets the running execution context's lexical_environment to a new Object Environment.
generator . start_boundary ( Bytecode : : Generator : : BlockBoundaryType : : LeaveLexicalEnvironment ) ;
2023-07-02 10:28:36 +02:00
2026-02-11 22:42:53 +01:00
auto body_result = m_body - > generate_bytecode ( generator ) ;
2024-02-04 08:00:54 +01:00
if ( ! body_result . has_value ( ) )
body_result = generator . add_constant ( js_undefined ( ) ) ;
2022-03-14 02:35:45 +00:00
generator . end_boundary ( Bytecode : : Generator : : BlockBoundaryType : : LeaveLexicalEnvironment ) ;
2026-02-09 03:34:42 +01:00
generator . pop_lexical_environment_register ( ) ;
2022-03-14 02:35:45 +00:00
if ( ! generator . is_current_block_terminated ( ) )
2026-02-09 03:34:42 +01:00
generator . emit < Bytecode : : Op : : SetLexicalEnvironment > ( generator . current_lexical_environment_register ( ) ) ;
2022-03-14 02:35:45 +00:00
2024-02-04 08:00:54 +01:00
return body_result ;
2022-03-13 16:01:18 +03:30
}
2022-03-18 20:18:19 +03:30
enum class LHSKind {
Assignment ,
VarBinding ,
LexicalBinding ,
} ;
enum class IterationKind {
Enumerate ,
Iterate ,
AsyncIterate ,
} ;
// 14.7.5.6 ForIn/OfHeadEvaluation ( uninitializedBoundNames, expr, iterationKind ), https://tc39.es/ecma262/#sec-runtime-semantics-forinofheadevaluation
struct ForInOfHeadEvaluationResult {
bool is_destructuring { false } ;
LHSKind lhs_kind { LHSKind : : Assignment } ;
2025-10-27 19:46:54 +01:00
Optional < ScopedOperand > iterator_object ;
Optional < ScopedOperand > iterator_next_method ;
Optional < ScopedOperand > iterator_done_property ;
2022-03-18 20:18:19 +03:30
} ;
2026-02-11 22:42:53 +01:00
static ForInOfHeadEvaluationResult for_in_of_head_evaluation ( Bytecode : : Generator & generator , IterationKind iteration_kind , Variant < NonnullRefPtr < ASTNode const > , NonnullRefPtr < BindingPattern const > > const & lhs , NonnullRefPtr < ASTNode const > const & rhs )
2022-03-18 20:18:19 +03:30
{
ForInOfHeadEvaluationResult result { } ;
2022-10-19 17:42:54 +02:00
bool entered_lexical_scope = false ;
2023-02-19 22:07:52 +01:00
if ( auto * ast_ptr = lhs . get_pointer < NonnullRefPtr < ASTNode const > > ( ) ; ast_ptr & & is < VariableDeclaration > ( * * ast_ptr ) ) {
2022-03-18 20:18:19 +03:30
// Runtime Semantics: ForInOfLoopEvaluation, for any of:
// ForInOfStatement : for ( var ForBinding in Expression ) Statement
// ForInOfStatement : for ( ForDeclaration in Expression ) Statement
// ForInOfStatement : for ( var ForBinding of AssignmentExpression ) Statement
// ForInOfStatement : for ( ForDeclaration of AssignmentExpression ) Statement
auto & variable_declaration = static_cast < VariableDeclaration const & > ( * * ast_ptr ) ;
2023-03-06 14:17:01 +01:00
result . is_destructuring = variable_declaration . declarations ( ) . first ( ) - > target ( ) . has < NonnullRefPtr < BindingPattern const > > ( ) ;
2022-03-18 20:18:19 +03:30
result . lhs_kind = variable_declaration . is_lexical_declaration ( ) ? LHSKind : : LexicalBinding : LHSKind : : VarBinding ;
2023-06-28 09:07:33 +02:00
if ( variable_declaration . declaration_kind ( ) = = DeclarationKind : : Var ) {
// B.3.5 Initializers in ForIn Statement Heads, https://tc39.es/ecma262/#sec-initializers-in-forin-statement-heads
auto & variable = variable_declaration . declarations ( ) . first ( ) ;
if ( variable - > init ( ) ) {
VERIFY ( variable - > target ( ) . has < NonnullRefPtr < Identifier const > > ( ) ) ;
2023-07-05 02:17:10 +02:00
auto identifier = variable - > target ( ) . get < NonnullRefPtr < Identifier const > > ( ) ;
auto identifier_table_ref = generator . intern_identifier ( identifier - > string ( ) ) ;
2026-02-11 22:42:53 +01:00
auto value = generator . emit_named_evaluation_if_anonymous_function ( * variable - > init ( ) , identifier_table_ref ) ;
2024-02-04 08:00:54 +01:00
generator . emit_set_variable ( * identifier , value ) ;
2023-06-28 09:07:33 +02:00
}
} else {
2023-07-13 21:30:36 +02:00
auto has_non_local_variables = false ;
MUST ( variable_declaration . for_each_bound_identifier ( [ & ] ( auto const & identifier ) {
if ( ! identifier . is_local ( ) )
has_non_local_variables = true ;
2023-02-27 22:13:37 +00:00
} ) ) ;
2023-07-13 21:30:36 +02:00
if ( has_non_local_variables ) {
// 1. Let oldEnv be the running execution context's LexicalEnvironment.
// NOTE: 'uninitializedBoundNames' refers to the lexical bindings (i.e. Const/Let) present in the second and last form.
// 2. If uninitializedBoundNames is not an empty List, then
entered_lexical_scope = true ;
// a. Assert: uninitializedBoundNames has no duplicate entries.
// b. Let newEnv be NewDeclarativeEnvironment(oldEnv).
generator . begin_variable_scope ( ) ;
// c. For each String name of uninitializedBoundNames, do
// NOTE: Nothing in the callback throws an exception.
MUST ( variable_declaration . for_each_bound_identifier ( [ & ] ( auto const & identifier ) {
if ( identifier . is_local ( ) )
return ;
// i. Perform ! newEnv.CreateMutableBinding(name, false).
auto interned_identifier = generator . intern_identifier ( identifier . string ( ) ) ;
2025-11-20 22:14:50 +01:00
generator . emit < Bytecode : : Op : : CreateVariable > ( interned_identifier , Bytecode : : Op : : EnvironmentMode : : Lexical , false , false , false ) ;
2023-07-13 21:30:36 +02:00
} ) ) ;
// d. Set the running execution context's LexicalEnvironment to newEnv.
// NOTE: Done by CreateLexicalEnvironment.
}
2022-03-18 20:18:19 +03:30
}
} else {
// Runtime Semantics: ForInOfLoopEvaluation, for any of:
// ForInOfStatement : for ( LeftHandSideExpression in Expression ) Statement
// ForInOfStatement : for ( LeftHandSideExpression of AssignmentExpression ) Statement
result . lhs_kind = LHSKind : : Assignment ;
2022-10-19 17:42:54 +02:00
}
2022-03-18 20:18:19 +03:30
2022-10-19 17:42:54 +02:00
// 3. Let exprRef be the result of evaluating expr.
2026-02-11 22:42:53 +01:00
auto object = rhs - > generate_bytecode ( generator ) . value ( ) ;
2022-10-19 17:42:54 +02:00
// 4. Set the running execution context's LexicalEnvironment to oldEnv.
if ( entered_lexical_scope )
generator . end_variable_scope ( ) ;
2022-03-18 20:18:19 +03:30
2022-10-19 17:42:54 +02:00
// 5. Let exprValue be ? GetValue(exprRef).
// NOTE: No need to store this anywhere.
2022-03-18 20:18:19 +03:30
2025-10-27 19:46:54 +01:00
auto iterator_object = generator . allocate_register ( ) ;
auto iterator_next_method = generator . allocate_register ( ) ;
auto iterator_done_property = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
2022-10-19 17:42:54 +02:00
// 6. If iterationKind is enumerate, then
if ( iteration_kind = = IterationKind : : Enumerate ) {
// a. If exprValue is undefined or null, then
auto & nullish_block = generator . make_block ( ) ;
auto & continuation_block = generator . make_block ( ) ;
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : JumpNullish > (
2024-02-04 08:00:54 +01:00
object ,
2023-09-28 12:43:11 +02:00
Bytecode : : Label { nullish_block } ,
Bytecode : : Label { continuation_block } ) ;
2022-10-19 17:42:54 +02:00
// i. Return Completion Record { [[Type]]: break, [[Value]]: empty, [[Target]]: empty }.
generator . switch_to_basic_block ( nullish_block ) ;
2022-11-25 16:42:29 +01:00
generator . generate_break ( ) ;
2022-10-19 17:42:54 +02:00
generator . switch_to_basic_block ( continuation_block ) ;
// b. Let obj be ! ToObject(exprValue).
// NOTE: GetObjectPropertyIterator does this.
// c. Let iterator be EnumerateObjectProperties(obj).
// d. Let nextMethod be ! GetV(iterator, "next").
// e. Return the Iterator Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
2025-10-27 19:46:54 +01:00
generator . emit < Bytecode : : Op : : GetObjectPropertyIterator > ( iterator_object , iterator_next_method , iterator_done_property , object ) ;
2022-10-19 17:42:54 +02:00
}
// 7. Else,
else {
2022-03-18 20:18:19 +03:30
// a. Assert: iterationKind is iterate or async-iterate.
2023-07-18 14:52:21 -04:00
// b. If iterationKind is async-iterate, let iteratorKind be async.
// c. Else, let iteratorKind be sync.
auto iterator_kind = iteration_kind = = IterationKind : : AsyncIterate ? IteratorHint : : Async : IteratorHint : : Sync ;
2022-03-18 20:18:19 +03:30
2023-07-18 14:52:21 -04:00
// d. Return ? GetIterator(exprValue, iteratorKind).
2025-10-27 19:46:54 +01:00
generator . emit < Bytecode : : Op : : GetIterator > ( iterator_object , iterator_next_method , iterator_done_property , object , iterator_kind ) ;
2022-03-18 20:18:19 +03:30
}
2025-10-27 19:46:54 +01:00
result . iterator_object = iterator_object ;
result . iterator_next_method = iterator_next_method ;
result . iterator_done_property = iterator_done_property ;
2022-03-18 20:18:19 +03:30
return result ;
}
// 14.7.5.7 ForIn/OfBodyEvaluation ( lhs, stmt, iteratorRecord, iterationKind, lhsKind, labelSet [ , iteratorKind ] ), https://tc39.es/ecma262/#sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset
2026-02-11 23:49:53 +01:00
static Optional < ScopedOperand > for_in_of_body_evaluation ( Bytecode : : Generator & generator , Variant < NonnullRefPtr < ASTNode const > , NonnullRefPtr < BindingPattern const > > const & lhs , ASTNode const & body , ForInOfHeadEvaluationResult const & head_result , IterationKind iteration_kind , Vector < FlyString > const & label_set , Bytecode : : BasicBlock & loop_end , Bytecode : : BasicBlock & loop_update , IteratorHint iterator_kind = IteratorHint : : Sync , [[maybe_unused]] Optional < ScopedOperand > preferred_dst = { } )
2022-03-18 20:18:19 +03:30
{
2023-06-27 19:24:34 +01:00
// 1. If iteratorKind is not present, set iteratorKind to sync.
2022-03-18 20:18:19 +03:30
// 2. Let oldEnv be the running execution context's LexicalEnvironment.
bool has_lexical_binding = false ;
// 3. Let V be undefined.
2024-05-14 10:57:06 +02:00
Optional < ScopedOperand > completion ;
if ( generator . must_propagate_completion ( ) ) {
completion = generator . allocate_register ( ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( * completion , generator . add_constant ( js_undefined ( ) ) ) ;
2024-05-14 10:57:06 +02:00
}
2022-03-18 20:18:19 +03:30
// 4. Let destructuring be IsDestructuring of lhs.
auto destructuring = head_result . is_destructuring ;
// 5. If destructuring is true and if lhsKind is assignment, then
2026-02-11 22:05:55 +01:00
// NB: is_destructuring is only set for VariableDeclaration lhs (which always has lhs_kind
// VarBinding or LexicalBinding), so this combination is unreachable.
VERIFY ( ! ( destructuring & & head_result . lhs_kind = = LHSKind : : Assignment ) ) ;
2026-02-10 23:25:24 +01:00
if ( completion . has_value ( ) )
generator . set_current_breakable_scope_completion_register ( * completion ) ;
2026-02-11 23:49:53 +01:00
// For for-of and for-await-of, set up a synthetic FinallyContext so that
// IteratorClose/AsyncIteratorClose is called on abrupt completion (break,
// return, throw, or continue-to-outer-loop). for-in (enumerate) does not
// need iterator close per spec.
bool needs_iterator_close = ( iteration_kind ! = IterationKind : : Enumerate ) ;
Optional < Bytecode : : Generator : : FinallyContext > iterator_close_finally_context ;
Optional < Bytecode : : Generator : : UnwindContext > iterator_close_unwind_context ;
Optional < ScopedOperand > close_completion_type ;
Optional < ScopedOperand > close_completion_value ;
Bytecode : : BasicBlock * exception_preamble_block { nullptr } ;
Bytecode : : BasicBlock * iterator_close_body_block { nullptr } ;
Optional < ScopedOperand > lexical_environment_at_entry ;
if ( needs_iterator_close ) {
lexical_environment_at_entry = generator . current_lexical_environment_register ( ) ;
close_completion_type = generator . allocate_register ( ) ;
close_completion_value = generator . allocate_register ( ) ;
exception_preamble_block = & generator . make_block ( ) ;
iterator_close_body_block = & generator . make_block ( ) ;
iterator_close_finally_context . emplace ( Bytecode : : Generator : : FinallyContext {
. completion_type = * close_completion_type ,
. completion_value = * close_completion_value ,
. finally_body = Bytecode : : Label { * iterator_close_body_block } ,
. exception_preamble = Bytecode : : Label { * exception_preamble_block } ,
. parent = generator . current_finally_context ( ) ,
. registered_jumps = { } ,
. next_jump_index = Bytecode : : Generator : : FinallyContext : : FIRST_JUMP_INDEX ,
. lexical_environment_at_entry = lexical_environment_at_entry ,
} ) ;
generator . set_current_finally_context ( & * iterator_close_finally_context ) ;
// Place ReturnToFinally between Break (pushed by caller) and Continue
// (pushed by begin_continuable_scope below). This ensures:
// - continue to this loop: hits Continue first -> direct jump (no close)
// - break/return/throw/continue-to-outer: hits ReturnToFinally -> close
generator . start_boundary ( Bytecode : : Generator : : BlockBoundaryType : : ReturnToFinally ) ;
// NB: The UnwindContext (exception handler) is set up later, after
// the iterator-next section. Per spec, exceptions from steps a-f
// (IteratorNext, Await, IteratorComplete, IteratorValue) propagate
// directly without calling IteratorClose. Only exceptions from
// LHS assignment (steps g-j) and the loop body (step l) should
// trigger iterator close.
}
2022-03-18 20:18:19 +03:30
// 6. Repeat,
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { loop_update } ) ;
generator . switch_to_basic_block ( loop_update ) ;
2026-02-10 23:25:24 +01:00
generator . begin_continuable_scope ( Bytecode : : Label { loop_update } , label_set , completion ) ;
2022-03-18 20:18:19 +03:30
// a. Let nextResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]).
2025-04-30 16:31:26 +03:00
auto next_value = generator . allocate_register ( ) ;
auto done = generator . allocate_register ( ) ;
if ( iterator_kind = = IteratorHint : : Sync ) {
2025-10-27 19:46:54 +01:00
generator . emit < Bytecode : : Op : : IteratorNextUnpack > ( next_value , done , * head_result . iterator_object , * head_result . iterator_next_method , * head_result . iterator_done_property ) ;
2025-04-30 16:31:26 +03:00
auto & loop_continue = generator . make_block ( ) ;
generator . emit_jump_if (
done ,
Bytecode : : Label { loop_end } ,
Bytecode : : Label { loop_continue } ) ;
generator . switch_to_basic_block ( loop_continue ) ;
} else {
auto next_result = generator . allocate_register ( ) ;
2025-10-27 19:46:54 +01:00
generator . emit < Bytecode : : Op : : IteratorNext > ( next_result , * head_result . iterator_object , * head_result . iterator_next_method , * head_result . iterator_done_property ) ;
2022-03-18 20:18:19 +03:30
2025-04-30 16:31:26 +03:00
// b. If iteratorKind is async, set nextResult to ? Await(nextResult).
2024-05-07 21:36:56 +02:00
auto received_completion = generator . allocate_register ( ) ;
auto received_completion_type = generator . allocate_register ( ) ;
auto received_completion_value = generator . allocate_register ( ) ;
2023-07-14 21:57:49 +01:00
2025-03-28 00:56:57 +01:00
generator . emit_mov ( received_completion , generator . accumulator ( ) ) ;
2025-03-31 09:32:39 +01:00
auto new_result = generate_await ( generator , next_result , received_completion , received_completion_type , received_completion_value ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( next_result , new_result ) ;
2022-03-18 20:18:19 +03:30
2025-04-30 16:31:26 +03:00
// c. If Type(nextResult) is not Object, throw a TypeError exception.
generator . emit < Bytecode : : Op : : ThrowIfNotObject > ( next_result ) ;
2022-03-18 20:18:19 +03:30
2025-04-30 16:31:26 +03:00
// d. Let done be ? IteratorComplete(nextResult).
generator . emit_iterator_complete ( done , next_result ) ;
2022-03-18 20:18:19 +03:30
2025-04-30 16:31:26 +03:00
// e. If done is true, return V.
auto & loop_continue = generator . make_block ( ) ;
generator . emit_jump_if (
done ,
Bytecode : : Label { loop_end } ,
Bytecode : : Label { loop_continue } ) ;
generator . switch_to_basic_block ( loop_continue ) ;
2022-03-18 20:18:19 +03:30
2025-04-30 16:31:26 +03:00
// f. Let nextValue be ? IteratorValue(nextResult).
generator . emit_iterator_value ( next_value , next_result ) ;
}
2022-03-18 20:18:19 +03:30
2026-02-11 23:49:53 +01:00
// Set up the exception handler now, after the iterator-next section.
// This ensures only LHS assignment and body exceptions trigger close.
// We must also switch to a fresh block so that subsequent code gets the
// new handler (make_block sets the handler at creation time).
if ( needs_iterator_close ) {
iterator_close_unwind_context . emplace ( generator , Bytecode : : Label { * exception_preamble_block } ) ;
auto & loop_body = generator . make_block ( ) ;
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { loop_body } ) ;
generator . switch_to_basic_block ( loop_body ) ;
}
2022-03-18 20:18:19 +03:30
// g. If lhsKind is either assignment or varBinding, then
if ( head_result . lhs_kind ! = LHSKind : : LexicalBinding ) {
// i. If destructuring is false, then
if ( ! destructuring ) {
// 1. Let lhsRef be the result of evaluating lhs. (It may be evaluated repeatedly.)
// NOTE: We're skipping all the completion stuff that the spec does, as the unwinding mechanism will take case of doing that.
if ( head_result . lhs_kind = = LHSKind : : VarBinding ) {
2023-02-19 22:07:52 +01:00
auto & declaration = static_cast < VariableDeclaration const & > ( * lhs . get < NonnullRefPtr < ASTNode const > > ( ) ) ;
2022-03-18 20:18:19 +03:30
VERIFY ( declaration . declarations ( ) . size ( ) = = 1 ) ;
2026-02-11 22:42:53 +01:00
assign_value_to_variable_declarator ( generator , declaration . declarations ( ) . first ( ) , declaration , next_value ) ;
2022-03-18 20:18:19 +03:30
} else {
2023-02-19 22:07:52 +01:00
if ( auto ptr = lhs . get_pointer < NonnullRefPtr < ASTNode const > > ( ) ) {
2026-02-11 22:42:53 +01:00
generator . emit_store_to_reference ( * * ptr , next_value ) ;
2022-03-31 03:09:36 +04:30
} else {
2023-02-19 22:07:52 +01:00
auto & binding_pattern = lhs . get < NonnullRefPtr < BindingPattern const > > ( ) ;
2026-02-11 22:42:53 +01:00
binding_pattern - > generate_bytecode ( generator , Bytecode : : Op : : BindingInitializationMode : : Set , next_value ) ;
2022-03-31 03:09:36 +04:30
}
2022-03-18 20:18:19 +03:30
}
}
}
// h. Else,
else {
// i. Assert: lhsKind is lexicalBinding.
// ii. Assert: lhs is a ForDeclaration.
// iii. Let iterationEnv be NewDeclarativeEnvironment(oldEnv).
// iv. Perform ForDeclarationBindingInstantiation of lhs with argument iterationEnv.
// v. Set the running execution context's LexicalEnvironment to iterationEnv.
// 14.7.5.4 Runtime Semantics: ForDeclarationBindingInstantiation, https://tc39.es/ecma262/#sec-runtime-semantics-fordeclarationbindinginstantiation
// 1. Assert: environment is a declarative Environment Record.
// NOTE: We just made it.
2023-02-19 22:07:52 +01:00
auto & variable_declaration = static_cast < VariableDeclaration const & > ( * lhs . get < NonnullRefPtr < ASTNode const > > ( ) ) ;
2022-03-18 20:18:19 +03:30
// 2. For each element name of the BoundNames of ForBinding, do
2023-02-27 22:13:37 +00:00
// NOTE: Nothing in the callback throws an exception.
2023-12-06 15:07:42 +01:00
auto has_non_local_variables = false ;
2023-07-13 21:30:36 +02:00
MUST ( variable_declaration . for_each_bound_identifier ( [ & ] ( auto const & identifier ) {
2023-12-06 15:07:42 +01:00
if ( ! identifier . is_local ( ) )
has_non_local_variables = true ;
2023-02-27 22:13:37 +00:00
} ) ) ;
2022-03-18 20:18:19 +03:30
2023-12-06 15:07:42 +01:00
if ( has_non_local_variables ) {
generator . begin_variable_scope ( ) ;
has_lexical_binding = true ;
MUST ( variable_declaration . for_each_bound_identifier ( [ & ] ( auto const & identifier ) {
if ( identifier . is_local ( ) )
return ;
auto interned_identifier = generator . intern_identifier ( identifier . string ( ) ) ;
// a. If IsConstantDeclaration of LetOrConst is true, then
if ( variable_declaration . is_constant_declaration ( ) ) {
// i. Perform ! environment.CreateImmutableBinding(name, true).
generator . emit < Bytecode : : Op : : CreateVariable > ( interned_identifier , Bytecode : : Op : : EnvironmentMode : : Lexical , true , false , true ) ;
}
// b. Else,
else {
// i. Perform ! environment.CreateMutableBinding(name, false).
2025-11-20 22:14:50 +01:00
generator . emit < Bytecode : : Op : : CreateVariable > ( interned_identifier , Bytecode : : Op : : EnvironmentMode : : Lexical , false , false , false ) ;
2023-12-06 15:07:42 +01:00
}
} ) ) ;
// 3. Return unused.
// NOTE: No need to do that as we've inlined this.
}
2022-03-18 20:18:19 +03:30
// vi. If destructuring is false, then
if ( ! destructuring ) {
// 1. Assert: lhs binds a single name.
// 2. Let lhsName be the sole element of BoundNames of lhs.
2023-07-05 02:17:10 +02:00
auto lhs_name = variable_declaration . declarations ( ) . first ( ) - > target ( ) . get < NonnullRefPtr < Identifier const > > ( ) ;
2022-03-18 20:18:19 +03:30
// 3. Let lhsRef be ! ResolveBinding(lhsName).
// NOTE: We're skipping all the completion stuff that the spec does, as the unwinding mechanism will take case of doing that.
2024-02-04 08:00:54 +01:00
2024-05-14 11:30:30 +02:00
generator . emit_set_variable ( * lhs_name , next_value , Bytecode : : Op : : BindingInitializationMode : : Initialize , Bytecode : : Op : : EnvironmentMode : : Lexical ) ;
2022-03-18 20:18:19 +03:30
}
}
// i. If destructuring is false, then
if ( ! destructuring ) {
// i. If lhsRef is an abrupt completion, then
// 1. Let status be lhsRef.
// ii. Else if lhsKind is lexicalBinding, then
// 1. Let status be Completion(InitializeReferencedBinding(lhsRef, nextValue)).
// iii. Else,
// 1. Let status be Completion(PutValue(lhsRef, nextValue)).
// NOTE: This is performed above.
}
// j. Else,
else {
2022-12-09 18:47:51 +00:00
// FIXME: i. If lhsKind is assignment, then
// 1. Let status be Completion(DestructuringAssignmentEvaluation of assignmentPattern with argument nextValue).
2022-03-18 20:18:19 +03:30
// ii. Else if lhsKind is varBinding, then
// 1. Assert: lhs is a ForBinding.
// 2. Let status be Completion(BindingInitialization of lhs with arguments nextValue and undefined).
// iii. Else,
// 1. Assert: lhsKind is lexicalBinding.
// 2. Assert: lhs is a ForDeclaration.
// 3. Let status be Completion(ForDeclarationBindingInitialization of lhs with arguments nextValue and iterationEnv).
2022-12-09 18:47:51 +00:00
if ( head_result . lhs_kind = = LHSKind : : VarBinding | | head_result . lhs_kind = = LHSKind : : LexicalBinding ) {
2023-02-19 22:07:52 +01:00
auto & declaration = static_cast < VariableDeclaration const & > ( * lhs . get < NonnullRefPtr < ASTNode const > > ( ) ) ;
2022-12-09 18:47:51 +00:00
VERIFY ( declaration . declarations ( ) . size ( ) = = 1 ) ;
2023-03-06 14:17:01 +01:00
auto & binding_pattern = declaration . declarations ( ) . first ( ) - > target ( ) . get < NonnullRefPtr < BindingPattern const > > ( ) ;
2026-02-11 22:42:53 +01:00
binding_pattern - > generate_bytecode (
2024-02-04 08:00:54 +01:00
generator ,
2024-05-14 11:30:30 +02:00
head_result . lhs_kind = = LHSKind : : VarBinding ? Bytecode : : Op : : BindingInitializationMode : : Set : Bytecode : : Op : : BindingInitializationMode : : Initialize ,
2026-02-11 22:42:53 +01:00
next_value ) ;
2022-12-09 18:47:51 +00:00
} else {
2026-02-11 22:05:55 +01:00
// NB: lhs_kind is Assignment only when is_destructuring is false, so this is unreachable.
VERIFY_NOT_REACHED ( ) ;
2022-12-09 18:47:51 +00:00
}
2022-03-18 20:18:19 +03:30
}
// k. If status is an abrupt completion, then
// i. Set the running execution context's LexicalEnvironment to oldEnv.
// ii. If iteratorKind is async, return ? AsyncIteratorClose(iteratorRecord, status).
// iii. If iterationKind is enumerate, then
// 1. Return ? status.
// iv. Else,
// 1. Assert: iterationKind is iterate.
// 2. Return ? IteratorClose(iteratorRecord, status).
2026-02-11 23:49:53 +01:00
// NB: Abrupt completions from LHS assignment and the loop body are handled
// by the synthetic FinallyContext set up above (for iterate/async-iterate).
2022-03-18 20:18:19 +03:30
// l. Let result be the result of evaluating stmt.
2026-03-01 13:17:26 +01:00
if ( ! generator . is_current_block_terminated ( ) ) {
2026-02-10 23:25:24 +01:00
Optional < Bytecode : : Generator : : CompletionRegisterScope > completion_scope ;
if ( completion . has_value ( ) )
completion_scope . emplace ( generator , * completion ) ;
2026-02-11 22:42:53 +01:00
auto result = body . generate_bytecode ( generator ) ;
2026-02-10 23:25:24 +01:00
if ( ! generator . is_current_block_terminated ( ) & & completion . has_value ( ) & & result . has_value ( ) )
generator . emit_mov ( * completion , * result ) ;
}
2023-07-05 14:07:20 +02:00
2022-03-18 20:18:19 +03:30
// m. Set the running execution context's LexicalEnvironment to oldEnv.
if ( has_lexical_binding )
generator . end_variable_scope ( ) ;
generator . end_continuable_scope ( ) ;
2026-02-11 23:49:53 +01:00
if ( needs_iterator_close ) {
generator . end_boundary ( Bytecode : : Generator : : BlockBoundaryType : : ReturnToFinally ) ;
generator . set_current_finally_context ( iterator_close_finally_context - > parent ) ;
iterator_close_unwind_context . clear ( ) ;
}
2022-03-18 20:18:19 +03:30
generator . end_breakable_scope ( ) ;
2022-03-27 18:46:25 +01:00
// The body can contain an unconditional block terminator (e.g. return, throw), so we have to check for that before generating the Jump.
2026-02-10 23:25:24 +01:00
if ( ! generator . is_current_block_terminated ( ) )
2023-09-28 12:43:11 +02:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { loop_update } ) ;
2022-03-27 18:46:25 +01:00
2026-02-11 23:49:53 +01:00
// Generate iterator close blocks for for-of/for-await-of.
if ( needs_iterator_close ) {
auto undefined_value = generator . add_constant ( js_undefined ( ) ) ;
// Exception preamble: catches thrown exceptions and routes to iterator close.
generator . switch_to_basic_block ( * exception_preamble_block ) ;
generator . emit < Bytecode : : Op : : Catch > ( * close_completion_value ) ;
generator . emit < Bytecode : : Op : : SetLexicalEnvironment > ( * lexical_environment_at_entry ) ;
generator . emit_mov ( * close_completion_type , generator . add_constant ( Value ( Bytecode : : Generator : : FinallyContext : : THROW ) ) ) ;
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { * iterator_close_body_block } ) ;
// Iterator close body: dispatch chain based on completion type.
generator . switch_to_basic_block ( * iterator_close_body_block ) ;
// THROW path: IteratorClose with Throw completion (original throw always wins).
auto & throw_close_block = generator . make_block ( ) ;
auto & non_throw_close_block = generator . make_block ( ) ;
generator . emit < Bytecode : : Op : : JumpStrictlyEquals > (
* close_completion_type , generator . add_constant ( Value ( Bytecode : : Generator : : FinallyContext : : THROW ) ) ,
Bytecode : : Label { throw_close_block } , Bytecode : : Label { non_throw_close_block } ) ;
// Non-throw abrupt path (break/return/continue-to-outer): close with Normal completion.
generator . switch_to_basic_block ( non_throw_close_block ) ;
2026-02-12 01:29:28 +01:00
if ( iterator_kind = = IteratorHint : : Async ) {
// For async iterators, we inline the AsyncIteratorClose steps
// using a proper Await op instead of the synchronous await()
// that the AsyncIteratorClose C++ op uses. The synchronous await
// spins the event loop inside bytecode execution, which violates
// the microtask checkpoint assertion.
auto & after_close = generator . make_block ( ) ;
// Spec: 7.4.13 AsyncIteratorClose ( iteratorRecord, completion )
// 3. Let innerResult be Completion(GetMethod(iterator, "return")).
auto return_method = generator . allocate_register ( ) ;
generator . emit < Bytecode : : Op : : GetMethod > ( return_method , * head_result . iterator_object , generator . intern_property_key ( " return " _utf16_fly_string ) ) ;
// 4a/b. If return is undefined, skip close.
auto & call_return_block = generator . make_block ( ) ;
generator . emit < Bytecode : : Op : : JumpUndefined > ( return_method , Bytecode : : Label { after_close } , Bytecode : : Label { call_return_block } ) ;
generator . switch_to_basic_block ( call_return_block ) ;
// 4c. Set innerResult to Completion(Call(return, iterator)).
auto inner_result = generator . allocate_register ( ) ;
generator . emit_with_extra_operand_slots < Bytecode : : Op : : Call > ( 0 , inner_result , return_method , * head_result . iterator_object , OptionalNone { } , ReadonlySpan < ScopedOperand > { } ) ;
// 4d. Set innerResult to Completion(Await(innerResult.[[Value]])).
auto received_completion = generator . allocate_register ( ) ;
auto received_completion_type = generator . allocate_register ( ) ;
auto received_completion_value = generator . allocate_register ( ) ;
auto awaited = generate_await ( generator , inner_result , received_completion , received_completion_type , received_completion_value ) ;
// 7. If Type(innerResult.[[Value]]) is not Object, throw a TypeError exception.
generator . emit < Bytecode : : Op : : ThrowIfNotObject > ( awaited ) ;
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { after_close } ) ;
generator . switch_to_basic_block ( after_close ) ;
} else {
2026-02-11 23:49:53 +01:00
generator . emit < Bytecode : : Op : : IteratorClose > ( * head_result . iterator_object , * head_result . iterator_next_method , * head_result . iterator_done_property , Completion : : Type : : Normal , undefined_value ) ;
2026-02-12 01:29:28 +01:00
}
2026-02-11 23:49:53 +01:00
// Dispatch registered jumps (break/continue targets, indices 3+).
for ( auto const & jump : iterator_close_finally_context - > registered_jumps ) {
auto & after_jump_check = generator . make_block ( ) ;
generator . emit < Bytecode : : Op : : JumpStrictlyEquals > (
* close_completion_type , generator . add_constant ( Value ( jump . index ) ) ,
jump . target , Bytecode : : Label { after_jump_check } ) ;
generator . switch_to_basic_block ( after_jump_check ) ;
}
// RETURN path.
auto & return_block = generator . make_block ( ) ;
auto & unreachable_block = generator . make_block ( ) ;
generator . emit < Bytecode : : Op : : JumpStrictlyEquals > (
* close_completion_type , generator . add_constant ( Value ( Bytecode : : Generator : : FinallyContext : : RETURN ) ) ,
Bytecode : : Label { return_block } , Bytecode : : Label { unreachable_block } ) ;
generator . switch_to_basic_block ( return_block ) ;
if ( iterator_close_finally_context - > parent ) {
// Nested finally: copy completion record to outer and jump to outer finally body.
auto & outer = * iterator_close_finally_context - > parent ;
generator . emit_mov ( outer . completion_type , * close_completion_type ) ;
generator . emit_mov ( outer . completion_value , * close_completion_value ) ;
generator . emit < Bytecode : : Op : : Jump > ( outer . finally_body ) ;
} else {
if ( generator . is_in_generator_function ( ) )
generator . emit < Bytecode : : Op : : Yield > ( OptionalNone { } , * close_completion_value ) ;
else
generator . emit < Bytecode : : Op : : Return > ( * close_completion_value ) ;
}
// Default: unreachable (all completion types have been dispatched).
generator . switch_to_basic_block ( unreachable_block ) ;
generator . emit < Bytecode : : Op : : Throw > ( * close_completion_value ) ;
// Throw close block: IteratorClose with Throw completion, then rethrow.
2026-02-12 01:29:28 +01:00
// Per spec step 5, the original throw always takes precedence.
2026-02-11 23:49:53 +01:00
generator . switch_to_basic_block ( throw_close_block ) ;
2026-02-12 01:29:28 +01:00
if ( iterator_kind = = IteratorHint : : Async ) {
// Inline AsyncIteratorClose with exception handler: any error from
// the close steps is discarded and the original exception is rethrown.
auto & rethrow_block = generator . make_block ( ) ;
auto & close_catch_block = generator . make_block ( ) ;
{
Bytecode : : Generator : : UnwindContext close_unwind ( generator , Bytecode : : Label { close_catch_block } ) ;
// Jump to a block created inside the UnwindContext so that
// GetMethod/Call/Await all have the exception handler set.
// throw_close_block was created before the UnwindContext and
// doesn't have the handler.
auto & close_try_block = generator . make_block ( ) ;
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { close_try_block } ) ;
generator . switch_to_basic_block ( close_try_block ) ;
auto return_method = generator . allocate_register ( ) ;
generator . emit < Bytecode : : Op : : GetMethod > ( return_method , * head_result . iterator_object , generator . intern_property_key ( " return " _utf16_fly_string ) ) ;
auto & call_return_block = generator . make_block ( ) ;
generator . emit < Bytecode : : Op : : JumpUndefined > ( return_method , Bytecode : : Label { rethrow_block } , Bytecode : : Label { call_return_block } ) ;
generator . switch_to_basic_block ( call_return_block ) ;
auto inner_result = generator . allocate_register ( ) ;
generator . emit_with_extra_operand_slots < Bytecode : : Op : : Call > ( 0 , inner_result , return_method , * head_result . iterator_object , OptionalNone { } , ReadonlySpan < ScopedOperand > { } ) ;
auto received_completion = generator . allocate_register ( ) ;
auto received_completion_type = generator . allocate_register ( ) ;
auto received_completion_value = generator . allocate_register ( ) ;
generate_await ( generator , inner_result , received_completion , received_completion_type , received_completion_value ) ;
// Even if close succeeded, rethrow original (spec step 5).
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { rethrow_block } ) ;
}
// Exception handler: discard close error, rethrow original.
generator . switch_to_basic_block ( close_catch_block ) ;
auto discarded = generator . allocate_register ( ) ;
generator . emit < Bytecode : : Op : : Catch > ( discarded ) ;
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { rethrow_block } ) ;
generator . switch_to_basic_block ( rethrow_block ) ;
2026-02-11 23:49:53 +01:00
generator . emit < Bytecode : : Op : : Throw > ( * close_completion_value ) ;
2026-02-12 01:29:28 +01:00
} else {
generator . emit < Bytecode : : Op : : IteratorClose > ( * head_result . iterator_object , * head_result . iterator_next_method , * head_result . iterator_done_property , Completion : : Type : : Throw , * close_completion_value ) ;
// iterator_close with Throw completion always re-throws, but if it
// somehow returns normally, rethrow the original exception.
if ( ! generator . is_current_block_terminated ( ) )
generator . emit < Bytecode : : Op : : Throw > ( * close_completion_value ) ;
}
2026-02-11 23:49:53 +01:00
}
2022-03-18 20:18:19 +03:30
generator . switch_to_basic_block ( loop_end ) ;
2024-05-14 10:57:06 +02:00
return completion ;
2022-03-18 20:18:19 +03:30
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ForInStatement : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2022-06-11 23:09:37 +01:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2022-06-11 23:09:37 +01:00
return generate_labelled_evaluation ( generator , { } ) ;
}
// 14.7.5.5 Runtime Semantics: ForInOfLoopEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-forinofloopevaluation
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ForInStatement : : generate_labelled_evaluation ( Bytecode : : Generator & generator , Vector < FlyString > const & label_set , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2022-03-18 20:18:19 +03:30
{
auto & loop_end = generator . make_block ( ) ;
auto & loop_update = generator . make_block ( ) ;
2022-06-11 23:09:37 +01:00
generator . begin_breakable_scope ( Bytecode : : Label { loop_end } , label_set ) ;
2022-03-18 20:18:19 +03:30
2026-02-11 22:42:53 +01:00
auto head_result = for_in_of_head_evaluation ( generator , IterationKind : : Enumerate , m_lhs , m_rhs ) ;
2026-02-11 23:49:53 +01:00
return for_in_of_body_evaluation ( generator , m_lhs , body ( ) , head_result , IterationKind : : Enumerate , label_set , loop_end , loop_update ) ;
2022-03-18 20:18:19 +03:30
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ForOfStatement : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2022-06-11 23:09:37 +01:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2022-06-11 23:09:37 +01:00
return generate_labelled_evaluation ( generator , { } ) ;
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ForOfStatement : : generate_labelled_evaluation ( Bytecode : : Generator & generator , Vector < FlyString > const & label_set , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2022-03-18 20:18:19 +03:30
{
auto & loop_end = generator . make_block ( ) ;
auto & loop_update = generator . make_block ( ) ;
2022-06-11 23:09:37 +01:00
generator . begin_breakable_scope ( Bytecode : : Label { loop_end } , label_set ) ;
2022-03-18 20:18:19 +03:30
2026-02-11 22:42:53 +01:00
auto head_result = for_in_of_head_evaluation ( generator , IterationKind : : Iterate , m_lhs , m_rhs ) ;
2026-02-11 23:49:53 +01:00
return for_in_of_body_evaluation ( generator , m_lhs , body ( ) , head_result , IterationKind : : Iterate , label_set , loop_end , loop_update ) ;
2022-03-18 20:18:19 +03:30
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ForAwaitOfStatement : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2023-06-27 19:24:34 +01:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2023-06-27 19:24:34 +01:00
return generate_labelled_evaluation ( generator , { } ) ;
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ForAwaitOfStatement : : generate_labelled_evaluation ( Bytecode : : Generator & generator , Vector < FlyString > const & label_set , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2023-06-27 19:24:34 +01:00
{
auto & loop_end = generator . make_block ( ) ;
auto & loop_update = generator . make_block ( ) ;
generator . begin_breakable_scope ( Bytecode : : Label { loop_end } , label_set ) ;
2026-02-11 22:42:53 +01:00
auto head_result = for_in_of_head_evaluation ( generator , IterationKind : : AsyncIterate , m_lhs , m_rhs ) ;
2026-02-11 23:49:53 +01:00
return for_in_of_body_evaluation ( generator , m_lhs , m_body , head_result , IterationKind : : AsyncIterate , label_set , loop_end , loop_update , IteratorHint : : Async ) ;
2023-06-27 19:24:34 +01:00
}
2022-03-19 19:40:21 +00:00
// 13.3.12.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-meta-properties-runtime-semantics-evaluation
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > MetaProperty : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2022-03-19 19:40:21 +00:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2022-03-19 19:40:21 +00:00
// NewTarget : new . target
if ( m_type = = MetaProperty : : Type : : NewTarget ) {
// 1. Return GetNewTarget().
2024-02-04 08:00:54 +01:00
auto dst = choose_dst ( generator , preferred_dst ) ;
generator . emit < Bytecode : : Op : : GetNewTarget > ( dst ) ;
return dst ;
2022-03-19 19:40:21 +00:00
}
// ImportMeta : import . meta
if ( m_type = = MetaProperty : : Type : : ImportMeta ) {
2024-02-04 08:00:54 +01:00
auto dst = choose_dst ( generator , preferred_dst ) ;
generator . emit < Bytecode : : Op : : GetImportMeta > ( dst ) ;
return dst ;
2022-03-19 19:40:21 +00:00
}
VERIFY_NOT_REACHED ( ) ;
}
2023-06-17 10:11:23 +02:00
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ClassFieldInitializerStatement : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2023-06-17 10:11:23 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2026-02-11 22:42:53 +01:00
auto value = generator . emit_named_evaluation_if_anonymous_function ( * m_expression , generator . intern_identifier ( m_class_field_identifier_name ) , preferred_dst ) ;
2023-06-17 10:11:23 +02:00
generator . perform_needed_unwinds < Bytecode : : Op : : Return > ( ) ;
2025-05-16 12:31:42 +12:00
generator . emit < Bytecode : : Op : : Return > ( value . operand ( ) ) ;
2024-02-04 08:00:54 +01:00
return value ;
2023-06-17 10:11:23 +02:00
}
2026-02-11 22:42:53 +01:00
static void generate_optional_chain ( Bytecode : : Generator & generator , OptionalChain const & optional_chain , ScopedOperand current_value , ScopedOperand current_base , [[maybe_unused]] Optional < ScopedOperand > preferred_dst )
2023-06-17 14:50:23 +01:00
{
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > new_current_value ;
2023-06-17 14:50:23 +01:00
if ( is < MemberExpression > ( optional_chain . base ( ) ) ) {
auto & member_expression = static_cast < MemberExpression const & > ( optional_chain . base ( ) ) ;
2026-02-11 22:42:53 +01:00
auto base_and_value = get_base_and_value_from_member_expression ( generator , member_expression ) ;
2024-02-04 08:00:54 +01:00
new_current_value = base_and_value . value ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( current_base , base_and_value . base ) ;
2023-06-17 14:50:23 +01:00
} else if ( is < OptionalChain > ( optional_chain . base ( ) ) ) {
auto & sub_optional_chain = static_cast < OptionalChain const & > ( optional_chain . base ( ) ) ;
2026-02-11 22:42:53 +01:00
generate_optional_chain ( generator , sub_optional_chain , current_value , current_base ) ;
2024-02-04 08:00:54 +01:00
new_current_value = current_value ;
2023-06-17 14:50:23 +01:00
} else {
2026-02-11 22:42:53 +01:00
new_current_value = optional_chain . base ( ) . generate_bytecode ( generator ) . value ( ) ;
2023-06-17 14:50:23 +01:00
}
2025-03-28 00:56:57 +01:00
generator . emit_mov ( current_value , * new_current_value ) ;
2023-06-17 14:50:23 +01:00
auto & load_undefined_and_jump_to_end_block = generator . make_block ( ) ;
auto & end_block = generator . make_block ( ) ;
for ( auto & reference : optional_chain . references ( ) ) {
auto is_optional = reference . visit ( [ ] ( auto & ref ) { return ref . mode ; } ) = = OptionalChain : : Mode : : Optional ;
if ( is_optional ) {
auto & not_nullish_block = generator . make_block ( ) ;
generator . emit < Bytecode : : Op : : JumpNullish > (
2024-02-04 08:00:54 +01:00
current_value ,
2023-06-17 14:50:23 +01:00
Bytecode : : Label { load_undefined_and_jump_to_end_block } ,
Bytecode : : Label { not_nullish_block } ) ;
generator . switch_to_basic_block ( not_nullish_block ) ;
}
2026-02-11 22:42:53 +01:00
reference . visit (
[ & ] ( OptionalChain : : Call const & call ) - > void {
auto arguments = arguments_to_array_for_call ( generator , call . arguments ) . value ( ) ;
2025-11-20 22:14:50 +01:00
generator . emit < Bytecode : : Op : : CallWithArgumentArray > ( current_value , current_value , current_base , arguments , OptionalNone { } ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( current_base , generator . add_constant ( js_undefined ( ) ) ) ;
2023-06-17 14:50:23 +01:00
} ,
2026-02-11 22:42:53 +01:00
[ & ] ( OptionalChain : : ComputedReference const & ref ) - > void {
2025-03-28 00:56:57 +01:00
generator . emit_mov ( current_base , current_value ) ;
2026-02-11 22:42:53 +01:00
auto property = ref . expression - > generate_bytecode ( generator ) . value ( ) ;
2025-04-03 14:18:24 +02:00
generator . emit_get_by_value ( current_value , current_value , property ) ;
2023-06-17 14:50:23 +01:00
} ,
2026-02-11 22:42:53 +01:00
[ & ] ( OptionalChain : : MemberReference const & ref ) - > void {
2025-03-28 00:56:57 +01:00
generator . emit_mov ( current_base , current_value ) ;
2025-12-11 07:57:09 -06:00
generator . emit_get_by_id ( current_value , current_value , generator . intern_property_key ( ref . identifier - > string ( ) ) ) ;
2023-06-17 14:50:23 +01:00
} ,
2026-02-11 22:42:53 +01:00
[ & ] ( OptionalChain : : PrivateMemberReference const & ref ) - > void {
2025-03-28 00:56:57 +01:00
generator . emit_mov ( current_base , current_value ) ;
2024-02-04 08:00:54 +01:00
generator . emit < Bytecode : : Op : : GetPrivateById > ( current_value , current_value , generator . intern_identifier ( ref . private_identifier - > string ( ) ) ) ;
2026-02-11 22:42:53 +01:00
} ) ;
2023-06-17 14:50:23 +01:00
}
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { end_block } ) ;
generator . switch_to_basic_block ( load_undefined_and_jump_to_end_block ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( current_value , generator . add_constant ( js_undefined ( ) ) ) ;
2023-06-17 14:50:23 +01:00
generator . emit < Bytecode : : Op : : Jump > ( Bytecode : : Label { end_block } ) ;
generator . switch_to_basic_block ( end_block ) ;
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > OptionalChain : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2023-06-17 14:50:23 +01:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2024-05-07 21:36:56 +02:00
auto current_base = generator . allocate_register ( ) ;
2024-02-04 08:00:54 +01:00
auto current_value = choose_dst ( generator , preferred_dst ) ;
2025-03-28 00:56:57 +01:00
generator . emit_mov ( current_base , generator . add_constant ( js_undefined ( ) ) ) ;
2026-02-11 22:42:53 +01:00
generate_optional_chain ( generator , * this , current_value , current_base ) ;
2024-02-04 08:00:54 +01:00
return current_value ;
2023-06-17 14:50:23 +01:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ImportCall : : generate_bytecode ( Bytecode : : Generator & generator , Optional < ScopedOperand > preferred_dst ) const
2023-06-24 15:22:16 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2026-02-11 22:42:53 +01:00
auto specifier = m_specifier - > generate_bytecode ( generator ) . value ( ) ;
2023-06-24 15:22:16 +02:00
2024-05-07 21:36:56 +02:00
Optional < ScopedOperand > options ;
2023-06-24 15:22:16 +02:00
if ( m_options ) {
2026-02-11 22:42:53 +01:00
options = m_options - > generate_bytecode ( generator ) . value ( ) ;
2023-06-24 15:22:16 +02:00
} else {
2024-02-04 08:00:54 +01:00
options = generator . add_constant ( js_undefined ( ) ) ;
2023-06-24 15:22:16 +02:00
}
2024-02-04 08:00:54 +01:00
auto dst = choose_dst ( generator , preferred_dst ) ;
generator . emit < Bytecode : : Op : : ImportCall > ( dst , specifier , * options ) ;
return dst ;
2023-06-24 15:22:16 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ExportStatement : : generate_bytecode ( Bytecode : : Generator & generator , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2023-06-24 16:07:43 +02:00
{
2023-09-01 16:53:55 +02:00
Bytecode : : Generator : : SourceLocationScope scope ( generator , * this ) ;
2023-06-24 16:07:43 +02:00
if ( ! is_default_export ( ) ) {
if ( m_statement ) {
return m_statement - > generate_bytecode ( generator ) ;
}
2024-05-07 21:36:56 +02:00
return Optional < ScopedOperand > { } ;
2023-06-24 16:07:43 +02:00
}
VERIFY ( m_statement ) ;
if ( is < FunctionDeclaration > ( * m_statement ) | | is < ClassDeclaration > ( * m_statement ) ) {
return m_statement - > generate_bytecode ( generator ) ;
}
// ExportDeclaration : export default AssignmentExpression ;
2026-02-21 16:20:36 +01:00
// Always initialize the *default* binding per step 5 of the spec.
2023-06-24 16:07:43 +02:00
VERIFY ( is < Expression > ( * m_statement ) ) ;
2026-02-11 22:42:53 +01:00
auto value = generator . emit_named_evaluation_if_anonymous_function ( static_cast < Expression const & > ( * m_statement ) , generator . intern_identifier ( " default " _utf16_fly_string ) ) ;
2024-05-14 11:30:30 +02:00
generator . emit < Bytecode : : Op : : InitializeLexicalBinding > (
2024-02-04 08:00:54 +01:00
generator . intern_identifier ( ExportStatement : : local_name_for_default ) ,
2024-05-14 11:30:30 +02:00
value ) ;
2024-02-04 08:00:54 +01:00
return value ;
2023-06-24 16:07:43 +02:00
}
2026-02-11 22:42:53 +01:00
Optional < ScopedOperand > ImportStatement : : generate_bytecode ( Bytecode : : Generator & , [[maybe_unused]] Optional < ScopedOperand > preferred_dst ) const
2023-06-27 20:46:41 +02:00
{
2024-05-07 21:36:56 +02:00
return Optional < ScopedOperand > { } ;
2023-06-27 20:46:41 +02:00
}
2021-06-04 11:31:13 +02:00
}