LibJS: Add ECMAScriptFunctionObject::create_from_function_node() helper

This gives us a shared entry point for every situation where we
instantiate a function based on a FunctionNode from the AST.
This commit is contained in:
Andreas Kling 2025-04-07 21:08:55 +02:00 committed by Andreas Kling
commit 4209b18b88
Notes: github-actions[bot] 2025-04-08 16:54:03 +00:00
7 changed files with 95 additions and 27 deletions

View file

@ -105,8 +105,7 @@ Value FunctionExpression::instantiate_ordinary_function_expression(VM& vm, FlySt
auto private_environment = vm.running_execution_context().private_environment; auto private_environment = vm.running_execution_context().private_environment;
auto closure = ECMAScriptFunctionObject::create(realm, used_name, source_text(), body(), parameters(), function_length(), local_variables_names(), environment, private_environment, kind(), is_strict_mode(), auto closure = ECMAScriptFunctionObject::create_from_function_node(*this, used_name, realm, environment, private_environment);
parsing_insights(), is_arrow_function());
// FIXME: 6. Perform SetFunctionName(closure, name). // FIXME: 6. Perform SetFunctionName(closure, name).
// FIXME: 7. Perform MakeConstructor(closure). // FIXME: 7. Perform MakeConstructor(closure).
@ -151,8 +150,12 @@ ThrowCompletionOr<ClassElement::ClassValue> ClassMethod::class_element_evaluatio
{ {
auto property_key_or_private_name = TRY(class_key_to_property_name(vm, *m_key, property_key)); auto property_key_or_private_name = TRY(class_key_to_property_name(vm, *m_key, property_key));
auto& method_function = *ECMAScriptFunctionObject::create(*vm.current_realm(), m_function->name(), m_function->source_text(), m_function->body(), m_function->parameters(), m_function->function_length(), m_function->local_variables_names(), vm.lexical_environment(), vm.running_execution_context().private_environment, m_function->kind(), m_function->is_strict_mode(), auto& method_function = *ECMAScriptFunctionObject::create_from_function_node(
m_function->parsing_insights(), m_function->is_arrow_function()); *m_function,
m_function->name(),
*vm.current_realm(),
vm.lexical_environment(),
vm.running_execution_context().private_environment);
auto method_value = Value(&method_function); auto method_value = Value(&method_function);
method_function.make_method(target); method_function.make_method(target);
@ -346,20 +349,12 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::create_class_const
// FIXME: Step 14.a is done in the parser. By using a synthetic super(...args) which does not call @@iterator of %Array.prototype% // FIXME: Step 14.a is done in the parser. By using a synthetic super(...args) which does not call @@iterator of %Array.prototype%
auto const& constructor = *m_constructor; auto const& constructor = *m_constructor;
auto class_constructor = ECMAScriptFunctionObject::create( auto class_constructor = ECMAScriptFunctionObject::create_from_function_node(
realm, constructor,
constructor.name(), constructor.name(),
constructor.source_text(), realm,
constructor.body(),
constructor.parameters(),
constructor.function_length(),
constructor.local_variables_names(),
vm.lexical_environment(), vm.lexical_environment(),
vm.running_execution_context().private_environment, vm.running_execution_context().private_environment);
constructor.kind(),
constructor.is_strict_mode(),
constructor.parsing_insights(),
constructor.is_arrow_function());
class_constructor->set_name(class_name); class_constructor->set_name(class_name);
class_constructor->set_home_object(prototype); class_constructor->set_home_object(prototype);
@ -1634,8 +1629,12 @@ void ScopeNode::block_declaration_instantiation(VM& vm, Environment* environment
auto& function_declaration = static_cast<FunctionDeclaration const&>(declaration); auto& function_declaration = static_cast<FunctionDeclaration const&>(declaration);
// ii. Let fo be InstantiateFunctionObject of d with arguments env and privateEnv. // ii. Let fo be InstantiateFunctionObject of d with arguments env and privateEnv.
auto function = ECMAScriptFunctionObject::create(realm, function_declaration.name(), function_declaration.source_text(), function_declaration.body(), function_declaration.parameters(), function_declaration.function_length(), function_declaration.local_variables_names(), environment, private_environment, function_declaration.kind(), function_declaration.is_strict_mode(), auto function = ECMAScriptFunctionObject::create_from_function_node(
function_declaration.parsing_insights()); function_declaration,
function_declaration.name(),
realm,
environment,
private_environment);
// iii. Perform ! env.InitializeBinding(fn, fo). NOTE: This step is replaced in section B.3.2.6. // iii. Perform ! env.InitializeBinding(fn, fo). NOTE: This step is replaced in section B.3.2.6.
if (function_declaration.name_identifier()->is_local()) { if (function_declaration.name_identifier()->is_local()) {
@ -1842,8 +1841,12 @@ ThrowCompletionOr<void> Program::global_declaration_instantiation(VM& vm, Global
for (auto& declaration : functions_to_initialize.in_reverse()) { for (auto& declaration : functions_to_initialize.in_reverse()) {
// a. Let fn be the sole element of the BoundNames of f. // a. Let fn be the sole element of the BoundNames of f.
// b. Let fo be InstantiateFunctionObject of f with arguments env and privateEnv. // b. Let fo be InstantiateFunctionObject of f with arguments env and privateEnv.
auto function = ECMAScriptFunctionObject::create(realm, declaration.name(), declaration.source_text(), declaration.body(), declaration.parameters(), declaration.function_length(), declaration.local_variables_names(), &global_environment, private_environment, declaration.kind(), declaration.is_strict_mode(), auto function = ECMAScriptFunctionObject::create_from_function_node(
declaration.parsing_insights()); declaration,
declaration.name(),
realm,
&global_environment,
private_environment);
// c. Perform ? env.CreateGlobalFunctionBinding(fn, fo, false). // c. Perform ? env.CreateGlobalFunctionBinding(fn, fo, false).
TRY(global_environment.create_global_function_binding(declaration.name(), function, false)); TRY(global_environment.create_global_function_binding(declaration.name(), function, false));

View file

@ -1305,8 +1305,12 @@ inline Value new_function(VM& vm, FunctionNode const& function_node, Optional<Id
name = vm.bytecode_interpreter().current_executable().get_identifier(lhs_name.value()); name = vm.bytecode_interpreter().current_executable().get_identifier(lhs_name.value());
value = function_node.instantiate_ordinary_function_expression(vm, name); value = function_node.instantiate_ordinary_function_expression(vm, name);
} else { } else {
value = ECMAScriptFunctionObject::create(*vm.current_realm(), function_node.name(), function_node.source_text(), function_node.body(), function_node.parameters(), function_node.function_length(), function_node.local_variables_names(), vm.lexical_environment(), vm.running_execution_context().private_environment, function_node.kind(), function_node.is_strict_mode(), value = ECMAScriptFunctionObject::create_from_function_node(
function_node.parsing_insights(), function_node.is_arrow_function()); function_node,
function_node.name(),
*vm.current_realm(),
vm.lexical_environment(),
vm.running_execution_context().private_environment);
} }
if (home_object.has_value()) { if (home_object.has_value()) {

View file

@ -972,8 +972,12 @@ ThrowCompletionOr<void> eval_declaration_instantiation(VM& vm, Program const& pr
for (auto& declaration : functions_to_initialize.in_reverse()) { for (auto& declaration : functions_to_initialize.in_reverse()) {
// a. Let fn be the sole element of the BoundNames of f. // a. Let fn be the sole element of the BoundNames of f.
// b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv. // b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv.
auto function = ECMAScriptFunctionObject::create(realm, declaration.name(), declaration.source_text(), declaration.body(), declaration.parameters(), declaration.function_length(), declaration.local_variables_names(), lexical_environment, private_environment, declaration.kind(), declaration.is_strict_mode(), auto function = ECMAScriptFunctionObject::create_from_function_node(
declaration.parsing_insights()); declaration,
declaration.name(),
realm,
lexical_environment,
private_environment);
// c. If varEnv is a global Environment Record, then // c. If varEnv is a global Environment Record, then
if (global_var_environment) { if (global_var_environment) {

View file

@ -58,6 +58,47 @@ GC::Ref<ECMAScriptFunctionObject> ECMAScriptFunctionObject::create(Realm& realm,
return realm.create<ECMAScriptFunctionObject>(move(name), move(source_text), ecmascript_code, move(parameters), m_function_length, move(local_variables_names), parent_environment, private_environment, prototype, kind, is_strict, parsing_insights, is_arrow_function, move(class_field_initializer_name)); return realm.create<ECMAScriptFunctionObject>(move(name), move(source_text), ecmascript_code, move(parameters), m_function_length, move(local_variables_names), parent_environment, private_environment, prototype, kind, is_strict, parsing_insights, is_arrow_function, move(class_field_initializer_name));
} }
GC::Ref<ECMAScriptFunctionObject> ECMAScriptFunctionObject::create_from_function_node(
FunctionNode const& function_node,
FlyString name,
GC::Ref<Realm> realm,
GC::Ptr<Environment> parent_environment,
GC::Ptr<PrivateEnvironment> private_environment)
{
GC::Ptr<Object> prototype = nullptr;
switch (function_node.kind()) {
case FunctionKind::Normal:
prototype = realm->intrinsics().function_prototype();
break;
case FunctionKind::Generator:
prototype = realm->intrinsics().generator_function_prototype();
break;
case FunctionKind::Async:
prototype = realm->intrinsics().async_function_prototype();
break;
case FunctionKind::AsyncGenerator:
prototype = realm->intrinsics().async_generator_function_prototype();
break;
}
return create(
*realm,
move(name),
*prototype,
function_node.source_text(),
*function_node.body_ptr(),
function_node.parameters(),
function_node.function_length(),
function_node.local_variables_names(),
parent_environment,
private_environment,
function_node.kind(),
function_node.is_strict_mode(),
function_node.parsing_insights(),
function_node.is_arrow_function(),
Variant<PropertyKey, PrivateName, Empty> {});
}
ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr<FunctionParameters const> formal_parameters, i32 function_length, Vector<FlyString> local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, Object& prototype, FunctionKind kind, bool strict, FunctionParsingInsights parsing_insights, bool is_arrow_function, Variant<PropertyKey, PrivateName, Empty> class_field_initializer_name) ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr<FunctionParameters const> formal_parameters, i32 function_length, Vector<FlyString> local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, Object& prototype, FunctionKind kind, bool strict, FunctionParsingInsights parsing_insights, bool is_arrow_function, Variant<PropertyKey, PrivateName, Empty> class_field_initializer_name)
: FunctionObject(prototype) : FunctionObject(prototype)
, m_name(move(name)) , m_name(move(name))

View file

@ -42,6 +42,13 @@ public:
static GC::Ref<ECMAScriptFunctionObject> create(Realm&, FlyString name, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr<FunctionParameters const> parameters, i32 m_function_length, Vector<FlyString> local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind, bool is_strict, FunctionParsingInsights, bool is_arrow_function = false, Variant<PropertyKey, PrivateName, Empty> class_field_initializer_name = {}); static GC::Ref<ECMAScriptFunctionObject> create(Realm&, FlyString name, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr<FunctionParameters const> parameters, i32 m_function_length, Vector<FlyString> local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind, bool is_strict, FunctionParsingInsights, bool is_arrow_function = false, Variant<PropertyKey, PrivateName, Empty> class_field_initializer_name = {});
static GC::Ref<ECMAScriptFunctionObject> create(Realm&, FlyString name, Object& prototype, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr<FunctionParameters const> parameters, i32 m_function_length, Vector<FlyString> local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind, bool is_strict, FunctionParsingInsights, bool is_arrow_function = false, Variant<PropertyKey, PrivateName, Empty> class_field_initializer_name = {}); static GC::Ref<ECMAScriptFunctionObject> create(Realm&, FlyString name, Object& prototype, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr<FunctionParameters const> parameters, i32 m_function_length, Vector<FlyString> local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind, bool is_strict, FunctionParsingInsights, bool is_arrow_function = false, Variant<PropertyKey, PrivateName, Empty> class_field_initializer_name = {});
[[nodiscard]] static GC::Ref<ECMAScriptFunctionObject> create_from_function_node(
FunctionNode const&,
FlyString name,
GC::Ref<Realm>,
GC::Ptr<Environment> parent_environment,
GC::Ptr<PrivateEnvironment>);
virtual void initialize(Realm&) override; virtual void initialize(Realm&) override;
virtual ~ECMAScriptFunctionObject() override = default; virtual ~ECMAScriptFunctionObject() override = default;

View file

@ -500,8 +500,12 @@ ThrowCompletionOr<void> SourceTextModule::initialize_environment(VM& vm)
FlyString function_name = function_declaration.name(); FlyString function_name = function_declaration.name();
if (function_name == ExportStatement::local_name_for_default) if (function_name == ExportStatement::local_name_for_default)
function_name = "default"_fly_string; function_name = "default"_fly_string;
auto function = ECMAScriptFunctionObject::create(realm(), function_name, function_declaration.source_text(), function_declaration.body(), function_declaration.parameters(), function_declaration.function_length(), function_declaration.local_variables_names(), environment, private_environment, function_declaration.kind(), function_declaration.is_strict_mode(), auto function = ECMAScriptFunctionObject::create_from_function_node(
function_declaration.parsing_insights()); function_declaration,
function_name,
realm(),
environment,
private_environment);
// 2. Perform ! env.InitializeBinding(dn, fo, normal). // 2. Perform ! env.InitializeBinding(dn, fo, normal).
MUST(environment->initialize_binding(vm, name, function, Environment::InitializeBindingHint::Normal)); MUST(environment->initialize_binding(vm, name, function, Environment::InitializeBindingHint::Normal));

View file

@ -67,7 +67,12 @@ static JS::ThrowCompletionOr<JS::Value> execute_a_function_body(HTML::BrowsingCo
// The result of parsing global scope above. // The result of parsing global scope above.
// strict // strict
// The result of parsing strict above. // The result of parsing strict above.
auto function = JS::ECMAScriptFunctionObject::create(realm, ""_fly_string, move(source_text), function_expression->body(), function_expression->parameters(), function_expression->function_length(), function_expression->local_variables_names(), &global_scope, nullptr, function_expression->kind(), function_expression->is_strict_mode(), function_expression->parsing_insights()); auto function = JS::ECMAScriptFunctionObject::create_from_function_node(
function_expression,
""_fly_string,
realm,
&global_scope,
nullptr);
// 9. Let completion be Function.[[Call]](window, parameters) with function as the this value. // 9. Let completion be Function.[[Call]](window, parameters) with function as the this value.
// NOTE: This is not entirely clear, but I don't think they mean actually passing `function` as // NOTE: This is not entirely clear, but I don't think they mean actually passing `function` as