2025-11-06 16:20:45 +00:00
/*
* Copyright ( c ) 2025 , Andreas Kling < andreas @ ladybird . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <LibJS/Runtime/SharedFunctionInstanceData.h>
# include <LibJS/Runtime/VM.h>
namespace JS {
GC_DEFINE_ALLOCATOR ( SharedFunctionInstanceData ) ;
SharedFunctionInstanceData : : SharedFunctionInstanceData (
VM & vm ,
FunctionKind kind ,
Utf16FlyString name ,
i32 function_length ,
NonnullRefPtr < FunctionParameters const > formal_parameters ,
NonnullRefPtr < Statement const > ecmascript_code ,
2025-12-20 14:23:47 -06:00
Utf16View source_text ,
2025-11-06 16:20:45 +00:00
bool strict ,
bool is_arrow_function ,
FunctionParsingInsights const & parsing_insights ,
Vector < LocalVariable > local_variables_names )
: m_formal_parameters ( move ( formal_parameters ) )
, m_ecmascript_code ( move ( ecmascript_code ) )
, m_name ( move ( name ) )
, m_source_text ( move ( source_text ) )
, m_local_variables_names ( move ( local_variables_names ) )
, m_function_length ( function_length )
, m_kind ( kind )
, m_strict ( strict )
, m_might_need_arguments_object ( parsing_insights . might_need_arguments_object )
, m_contains_direct_call_to_eval ( parsing_insights . contains_direct_call_to_eval )
, m_is_arrow_function ( is_arrow_function )
, m_uses_this ( parsing_insights . uses_this )
{
if ( m_is_arrow_function )
m_this_mode = ThisMode : : Lexical ;
else if ( m_strict )
m_this_mode = ThisMode : : Strict ;
else
m_this_mode = ThisMode : : Global ;
// 15.1.3 Static Semantics: IsSimpleParameterList, https://tc39.es/ecma262/#sec-static-semantics-issimpleparameterlist
m_has_simple_parameter_list = all_of ( m_formal_parameters - > parameters ( ) , [ & ] ( auto & parameter ) {
if ( parameter . is_rest )
return false ;
if ( parameter . default_value )
return false ;
if ( ! parameter . binding . template has < NonnullRefPtr < Identifier const > > ( ) )
return false ;
return true ;
} ) ;
// NOTE: The following steps are from FunctionDeclarationInstantiation that could be executed once
// and then reused in all subsequent function instantiations.
// 2. Let code be func.[[ECMAScriptCode]].
ScopeNode const * scope_body = nullptr ;
if ( is < ScopeNode > ( * m_ecmascript_code ) )
scope_body = static_cast < ScopeNode const * > ( m_ecmascript_code . ptr ( ) ) ;
// 3. Let strict be func.[[Strict]].
// 4. Let formals be func.[[FormalParameters]].
auto const & formals = * m_formal_parameters ;
// 5. Let parameterNames be the BoundNames of formals.
// 6. If parameterNames has any duplicate entries, let hasDuplicates be true. Otherwise, let hasDuplicates be false.
size_t parameters_in_environment = 0 ;
// NOTE: This loop performs step 5, 6, and 8.
for ( auto const & parameter : formals . parameters ( ) ) {
if ( parameter . default_value )
m_has_parameter_expressions = true ;
parameter . binding . visit (
[ & ] ( Identifier const & identifier ) {
if ( m_parameter_names . set ( identifier . string ( ) , identifier . is_local ( ) ? ParameterIsLocal : : Yes : ParameterIsLocal : : No ) ! = AK : : HashSetResult : : InsertedNewEntry )
m_has_duplicates = true ;
else if ( ! identifier . is_local ( ) )
+ + parameters_in_environment ;
} ,
[ & ] ( NonnullRefPtr < BindingPattern const > const & pattern ) {
if ( pattern - > contains_expression ( ) )
m_has_parameter_expressions = true ;
// NOTE: Nothing in the callback throws an exception.
MUST ( pattern - > for_each_bound_identifier ( [ & ] ( auto & identifier ) {
if ( m_parameter_names . set ( identifier . string ( ) , identifier . is_local ( ) ? ParameterIsLocal : : Yes : ParameterIsLocal : : No ) ! = AK : : HashSetResult : : InsertedNewEntry )
m_has_duplicates = true ;
else if ( ! identifier . is_local ( ) )
+ + parameters_in_environment ;
} ) ) ;
} ) ;
}
// 15. Let argumentsObjectNeeded be true.
m_arguments_object_needed = m_might_need_arguments_object ;
// 16. If func.[[ThisMode]] is lexical, then
if ( m_this_mode = = ThisMode : : Lexical ) {
// a. NOTE: Arrow functions never have an arguments object.
// b. Set argumentsObjectNeeded to false.
m_arguments_object_needed = false ;
}
// 17. Else if parameterNames contains "arguments", then
else if ( m_parameter_names . contains ( vm . names . arguments . as_string ( ) ) ) {
// a. Set argumentsObjectNeeded to false.
m_arguments_object_needed = false ;
}
// 18. Else if hasParameterExpressions is false, then
// a. If functionNames contains "arguments" or lexicalNames contains "arguments", then
// i. Set argumentsObjectNeeded to false.
// NOTE: The block below is a combination of step 14 and step 18.
2026-01-25 20:18:38 +01:00
if ( ! scope_body ) {
m_arguments_object_needed = false ;
} else {
scope_body - > ensure_function_scope_data ( ) ;
auto const & function_scope_data = * scope_body - > function_scope_data ( ) ;
2025-11-06 16:20:45 +00:00
2026-01-25 20:18:38 +01:00
for ( auto const & decl : function_scope_data . functions_to_initialize )
m_functions_to_initialize . append ( * decl ) ;
2025-11-06 16:20:45 +00:00
2026-01-25 20:18:38 +01:00
if ( ! m_has_parameter_expressions & & function_scope_data . has_function_named_arguments )
2025-11-06 16:20:45 +00:00
m_arguments_object_needed = false ;
2026-01-25 20:18:38 +01:00
if ( ! m_has_parameter_expressions & & m_arguments_object_needed & & function_scope_data . has_lexically_declared_arguments )
m_arguments_object_needed = false ;
2025-11-06 16:20:45 +00:00
}
2026-01-25 20:18:38 +01:00
auto arguments_object_needs_binding = m_arguments_object_needed & & ! m_local_variables_names . contains ( [ ] ( auto const & local ) { return local . declaration_kind = = LocalVariable : : DeclarationKind : : ArgumentsObject ; } ) ;
2025-11-06 16:20:45 +00:00
2026-01-25 20:18:38 +01:00
size_t * environment_size = nullptr ;
2025-11-06 16:20:45 +00:00
size_t parameter_environment_bindings_count = 0 ;
// 19. If strict is true or hasParameterExpressions is false, then
if ( strict | | ! m_has_parameter_expressions ) {
// a. NOTE: Only a single Environment Record is needed for the parameters, since calls to eval in strict mode code cannot create new bindings which are visible outside of the eval.
// b. Let env be the LexicalEnvironment of calleeContext
// NOTE: Here we are only interested in the size of the environment.
environment_size = & m_function_environment_bindings_count ;
}
// 20. Else,
else {
// a. NOTE: A separate Environment Record is needed to ensure that bindings created by direct eval calls in the formal parameter list are outside the environment where parameters are declared.
// b. Let calleeEnv be the LexicalEnvironment of calleeContext.
// c. Let env be NewDeclarativeEnvironment(calleeEnv).
environment_size = & parameter_environment_bindings_count ;
}
* environment_size + = parameters_in_environment ;
// 22. If argumentsObjectNeeded is true, then
2026-01-25 20:18:38 +01:00
if ( arguments_object_needs_binding )
( * environment_size ) + + ;
2025-11-06 16:20:45 +00:00
size_t * var_environment_size = nullptr ;
2026-01-25 20:18:38 +01:00
if ( scope_body ) {
auto const & function_scope_data = * scope_body - > function_scope_data ( ) ;
// 27. If hasParameterExpressions is false, then
if ( ! m_has_parameter_expressions ) {
// Use pre-computed non_local_var_count for environment size.
* environment_size + = function_scope_data . non_local_var_count ;
// Directly iterate vars_to_initialize - already deduplicated by parser.
for ( auto const & var : function_scope_data . vars_to_initialize ) {
// Skip vars that shadow parameters or "arguments" if needed.
if ( var . is_parameter )
continue ;
if ( var . identifier . string ( ) = = vm . names . arguments . as_string ( ) & & m_arguments_object_needed )
continue ;
m_var_names_to_initialize_binding . append ( {
. identifier = var . identifier ,
} ) ;
}
2025-11-06 16:20:45 +00:00
2026-01-25 20:18:38 +01:00
// d. Let varEnv be env
var_environment_size = environment_size ;
} else {
// a. NOTE: A separate Environment Record is needed to ensure that closures created by
// expressions in the formal parameter list do not have visibility of declarations in the function body.
// b. Let varEnv be NewDeclarativeEnvironment(env).
var_environment_size = & m_var_environment_bindings_count ;
// Use pre-computed non_local_var_count_for_parameter_expressions for environment size.
* var_environment_size + = function_scope_data . non_local_var_count_for_parameter_expressions ;
// Directly iterate vars_to_initialize - already deduplicated by parser.
for ( auto const & var : function_scope_data . vars_to_initialize ) {
bool is_in_parameter_bindings = var . is_parameter | | ( var . identifier . string ( ) = = vm . names . arguments . as_string ( ) & & m_arguments_object_needed ) ;
m_var_names_to_initialize_binding . append ( {
. identifier = var . identifier ,
. parameter_binding = is_in_parameter_bindings ,
. function_name = var . is_function_name ,
} ) ;
}
}
2025-11-06 16:20:45 +00:00
2026-01-25 20:18:38 +01:00
// 29. NOTE: Annex B.3.2.1 adds additional steps at this point.
// B.3.2.1 Changes to FunctionDeclarationInstantiation, https://tc39.es/ecma262/#sec-web-compat-functiondeclarationinstantiation
if ( ! m_strict ) {
HashTable < Utf16FlyString > annexB_seen_names ;
MUST ( scope_body - > for_each_function_hoistable_with_annexB_extension ( [ & ] ( FunctionDeclaration & function_declaration ) {
auto function_name = function_declaration . name ( ) ;
// Check if function name is in parameter_bindings (parameters + "arguments" if needed).
if ( m_parameter_names . contains ( function_name ) )
return ;
if ( function_name = = vm . names . arguments . as_string ( ) & & m_arguments_object_needed )
return ;
// Check if function name is already a var or already processed by AnnexB.
if ( ! function_scope_data . var_names . contains ( function_name ) & & ! annexB_seen_names . contains ( function_name ) ) {
m_function_names_to_initialize_binding . append ( function_name ) ;
annexB_seen_names . set ( function_name ) ;
( * var_environment_size ) + + ;
2025-11-06 16:20:45 +00:00
}
2026-01-25 20:18:38 +01:00
function_declaration . set_should_do_additional_annexB_steps ( ) ;
2025-11-06 16:20:45 +00:00
} ) ) ;
}
2026-01-25 20:18:38 +01:00
} else {
var_environment_size = environment_size ;
2025-11-06 16:20:45 +00:00
}
size_t * lex_environment_size = nullptr ;
// 30. If strict is false, then
if ( ! m_strict ) {
bool can_elide_declarative_environment = ! m_contains_direct_call_to_eval & & ( ! scope_body | | ! scope_body - > has_non_local_lexical_declarations ( ) ) ;
if ( can_elide_declarative_environment ) {
lex_environment_size = var_environment_size ;
} else {
// a. Let lexEnv be NewDeclarativeEnvironment(varEnv).
lex_environment_size = & m_lex_environment_bindings_count ;
}
} else {
// a. let lexEnv be varEnv.
// NOTE: Here we are only interested in the size of the environment.
lex_environment_size = var_environment_size ;
}
if ( scope_body ) {
MUST ( scope_body - > for_each_lexically_declared_identifier ( [ & ] ( auto const & id ) {
if ( ! id . is_local ( ) )
( * lex_environment_size ) + + ;
} ) ) ;
}
m_function_environment_needed = arguments_object_needs_binding | | m_function_environment_bindings_count > 0 | | m_var_environment_bindings_count > 0 | | m_lex_environment_bindings_count > 0 | | parsing_insights . uses_this_from_environment | | m_contains_direct_call_to_eval ;
}
void SharedFunctionInstanceData : : visit_edges ( Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
visitor . visit ( m_executable ) ;
2026-01-06 00:36:34 +01:00
m_class_field_initializer_name . visit ( [ & ] ( PropertyKey const & key ) { key . visit_edges ( visitor ) ; } , [ ] ( auto & ) { } ) ;
2025-11-06 16:20:45 +00:00
}
SharedFunctionInstanceData : : ~ SharedFunctionInstanceData ( ) = default ;
}