mirror of
https://github.com/RGBCube/serenity
synced 2025-05-18 21:25:07 +00:00
LibJS: Run reusable steps of FunctionDeclarationInstantiation only once
This change moves steps that can be executed only once and then reused in subsequent function instantiations from `function_declaration_instantiation` to the ECMAScriptFunctionObject: - Determine if there are any parameters with duplicate names. - Determine if there are any parameters with expressions. - Determine if an arguments object needs to be created. - Create a list of distinct function names for which bindings need to be created. - Create a list of distinct variable names for which bindings need to be created. This change makes React-Redux-TodoMVC test in Speedometer run 10% faster :)
This commit is contained in:
parent
d27e4732b6
commit
edd2f8b37f
2 changed files with 244 additions and 169 deletions
|
@ -99,6 +99,160 @@ ECMAScriptFunctionObject::ECMAScriptFunctionObject(DeprecatedFlyString name, Dep
|
|||
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.
|
||||
|
||||
// NOTE: This loop performs step 5, 6, and 8.
|
||||
for (auto const& parameter : formals) {
|
||||
if (parameter.default_value)
|
||||
m_has_parameter_expressions = true;
|
||||
|
||||
parameter.binding.visit(
|
||||
[&](Identifier const& identifier) {
|
||||
if (m_parameter_names.set(identifier.string()) != AK::HashSetResult::InsertedNewEntry)
|
||||
m_has_duplicates = true;
|
||||
},
|
||||
[&](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()) != AK::HashSetResult::InsertedNewEntry)
|
||||
m_has_duplicates = true;
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
// 15. Let argumentsObjectNeeded be true.
|
||||
m_arguments_object_needed = m_might_need_arguments_object;
|
||||
|
||||
// 16. If func.[[ThisMode]] is lexical, then
|
||||
if (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;
|
||||
}
|
||||
|
||||
HashTable<DeprecatedFlyString> function_names;
|
||||
|
||||
// 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.
|
||||
if (scope_body) {
|
||||
// NOTE: Nothing in the callback throws an exception.
|
||||
MUST(scope_body->for_each_var_function_declaration_in_reverse_order([&](FunctionDeclaration const& function) {
|
||||
if (function_names.set(function.name()) == AK::HashSetResult::InsertedNewEntry)
|
||||
m_functions_to_initialize.append(function);
|
||||
}));
|
||||
|
||||
auto const& arguments_name = vm().names.arguments.as_string();
|
||||
|
||||
if (!m_has_parameter_expressions && function_names.contains(arguments_name))
|
||||
m_arguments_object_needed = false;
|
||||
|
||||
if (!m_has_parameter_expressions && m_arguments_object_needed) {
|
||||
// NOTE: Nothing in the callback throws an exception.
|
||||
MUST(scope_body->for_each_lexically_declared_identifier([&](auto const& identifier) {
|
||||
if (identifier.string() == arguments_name)
|
||||
m_arguments_object_needed = false;
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
m_arguments_object_needed = false;
|
||||
}
|
||||
|
||||
HashTable<DeprecatedFlyString> parameter_bindings;
|
||||
|
||||
// 22. If argumentsObjectNeeded is true, then
|
||||
if (m_arguments_object_needed) {
|
||||
// f. Let parameterBindings be the list-concatenation of parameterNames and « "arguments" ».
|
||||
parameter_bindings = m_parameter_names;
|
||||
parameter_bindings.set(vm().names.arguments.as_string());
|
||||
} else {
|
||||
parameter_bindings = m_parameter_names;
|
||||
// a. Let parameterBindings be parameterNames.
|
||||
}
|
||||
|
||||
HashTable<DeprecatedFlyString> instantiated_var_names;
|
||||
|
||||
// 27. If hasParameterExpressions is false, then
|
||||
if (!m_has_parameter_expressions) {
|
||||
// b. Let instantiatedVarNames be a copy of the List parameterBindings.
|
||||
instantiated_var_names = parameter_bindings;
|
||||
|
||||
if (scope_body) {
|
||||
// c. For each element n of varNames, do
|
||||
MUST(scope_body->for_each_var_declared_identifier([&](auto const& id) {
|
||||
// i. If instantiatedVarNames does not contain n, then
|
||||
if (instantiated_var_names.set(id.string()) == AK::HashSetResult::InsertedNewEntry) {
|
||||
// 1. Append n to instantiatedVarNames.
|
||||
// Following steps will be executed in function_declaration_instantiation:
|
||||
// 2. Perform ! env.CreateMutableBinding(n, false).
|
||||
// 3. Perform ! env.InitializeBinding(n, undefined).
|
||||
m_var_names_to_initialize_binding.append({
|
||||
.identifier = id,
|
||||
.parameter_binding = parameter_bindings.contains(id.string()),
|
||||
.function_name = function_names.contains(id.string()),
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
// 28. Else,
|
||||
// NOTE: Steps a, b, c and d are executed in function_declaration_instantiation.
|
||||
// e. For each element n of varNames, do
|
||||
if (scope_body) {
|
||||
MUST(scope_body->for_each_var_declared_identifier([&](auto const& id) {
|
||||
// 1. Append n to instantiatedVarNames.
|
||||
// Following steps will be executed in function_declaration_instantiation:
|
||||
// 2. Perform ! env.CreateMutableBinding(n, false).
|
||||
// 3. Perform ! env.InitializeBinding(n, undefined).
|
||||
if (instantiated_var_names.set(id.string()) == AK::HashSetResult::InsertedNewEntry) {
|
||||
m_var_names_to_initialize_binding.append({
|
||||
.identifier = id,
|
||||
.parameter_binding = parameter_bindings.contains(id.string()),
|
||||
.function_name = function_names.contains(id.string()),
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_strict && scope_body) {
|
||||
MUST(scope_body->for_each_function_hoistable_with_annexB_extension([&](FunctionDeclaration& function_declaration) {
|
||||
auto function_name = function_declaration.name();
|
||||
if (parameter_bindings.contains(function_name))
|
||||
return;
|
||||
|
||||
if (!instantiated_var_names.contains(function_name) && function_name != vm().names.arguments.as_string()) {
|
||||
m_function_names_to_initialize_binding.append(function_name);
|
||||
instantiated_var_names.set(function_name);
|
||||
}
|
||||
|
||||
function_declaration.set_should_do_additional_annexB_steps();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void ECMAScriptFunctionObject::initialize(Realm& realm)
|
||||
|
@ -340,115 +494,32 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
if (is<ScopeNode>(*m_ecmascript_code))
|
||||
scope_body = static_cast<ScopeNode const*>(m_ecmascript_code.ptr());
|
||||
|
||||
// 3. Let strict be func.[[Strict]].
|
||||
bool const strict = is_strict_mode();
|
||||
|
||||
bool has_parameter_expressions = false;
|
||||
|
||||
// 4. Let formals be func.[[FormalParameters]].
|
||||
auto const& formals = m_formal_parameters;
|
||||
|
||||
// FIXME: Maybe compute has duplicates at parse time? (We need to anyway since it's an error in some cases)
|
||||
// 5. Let parameterNames be the BoundNames of formals.
|
||||
// 6. If parameterNames has any duplicate entries, let hasDuplicates be true. Otherwise, let hasDuplicates be false.
|
||||
bool has_duplicates = false;
|
||||
HashTable<DeprecatedFlyString> parameter_names;
|
||||
|
||||
// NOTE: This loop performs step 5, 6, and 8.
|
||||
for (auto const& parameter : formals) {
|
||||
if (parameter.default_value)
|
||||
has_parameter_expressions = true;
|
||||
|
||||
parameter.binding.visit(
|
||||
[&](Identifier const& identifier) {
|
||||
if (parameter_names.set(identifier.string()) != AK::HashSetResult::InsertedNewEntry)
|
||||
has_duplicates = true;
|
||||
},
|
||||
[&](NonnullRefPtr<BindingPattern const> const& pattern) {
|
||||
if (pattern->contains_expression())
|
||||
has_parameter_expressions = true;
|
||||
|
||||
// NOTE: Nothing in the callback throws an exception.
|
||||
MUST(pattern->for_each_bound_identifier([&](auto& identifier) {
|
||||
if (parameter_names.set(identifier.string()) != AK::HashSetResult::InsertedNewEntry)
|
||||
has_duplicates = true;
|
||||
}));
|
||||
});
|
||||
}
|
||||
// NOTE: Following steps were executed in ECMAScriptFunctionObject constructor.
|
||||
// 3. Let strict be func.[[Strict]].
|
||||
// 4. Let formals be func.[[FormalParameters]].
|
||||
// 5. Let parameterNames be the BoundNames of formals.
|
||||
// 6. If parameterNames has any duplicate entries, let hasDuplicates be true. Otherwise, let hasDuplicates be false.
|
||||
|
||||
// 7. Let simpleParameterList be IsSimpleParameterList of formals.
|
||||
bool const simple_parameter_list = has_simple_parameter_list();
|
||||
|
||||
// 8. Let hasParameterExpressions be ContainsExpression of formals.
|
||||
// NOTE: Already set above.
|
||||
|
||||
// 9. Let varNames be the VarDeclaredNames of code.
|
||||
// 10. Let varDeclarations be the VarScopedDeclarations of code.
|
||||
// 11. Let lexicalNames be the LexicallyDeclaredNames of code.
|
||||
// NOTE: Not needed as we use iteration helpers for this instead.
|
||||
|
||||
// 12. Let functionNames be a new empty List.
|
||||
HashTable<DeprecatedFlyString> function_names;
|
||||
|
||||
// 13. Let functionsToInitialize be a new empty List.
|
||||
Vector<FunctionDeclaration const&> functions_to_initialize;
|
||||
|
||||
// 14. For each element d of varDeclarations, in reverse List order, do
|
||||
// a. If d is neither a VariableDeclaration nor a ForBinding nor a BindingIdentifier, then
|
||||
// i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.
|
||||
// ii. Let fn be the sole element of the BoundNames of d.
|
||||
// iii. If functionNames does not contain fn, then
|
||||
// 1. Insert fn as the first element of functionNames.
|
||||
// 2. NOTE: If there are multiple function declarations for the same name, the last declaration is used.
|
||||
// 3. Insert d as the first element of functionsToInitialize.
|
||||
// NOTE: This block is done in step 18 below.
|
||||
|
||||
// 15. Let argumentsObjectNeeded be true.
|
||||
auto arguments_object_needed = m_might_need_arguments_object;
|
||||
|
||||
// 16. If func.[[ThisMode]] is lexical, then
|
||||
if (this_mode() == ThisMode::Lexical) {
|
||||
// a. NOTE: Arrow functions never have an arguments object.
|
||||
// b. Set argumentsObjectNeeded to false.
|
||||
arguments_object_needed = false;
|
||||
}
|
||||
// 17. Else if parameterNames contains "arguments", then
|
||||
else if (parameter_names.contains(vm.names.arguments.as_string())) {
|
||||
// a. Set argumentsObjectNeeded to false.
|
||||
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.
|
||||
if (scope_body) {
|
||||
// NOTE: Nothing in the callback throws an exception.
|
||||
MUST(scope_body->for_each_var_function_declaration_in_reverse_order([&](FunctionDeclaration const& function) {
|
||||
if (function_names.set(function.name()) == AK::HashSetResult::InsertedNewEntry)
|
||||
functions_to_initialize.append(function);
|
||||
}));
|
||||
|
||||
auto const& arguments_name = vm.names.arguments.as_string();
|
||||
|
||||
if (!has_parameter_expressions && function_names.contains(arguments_name))
|
||||
arguments_object_needed = false;
|
||||
|
||||
if (!has_parameter_expressions && arguments_object_needed) {
|
||||
// NOTE: Nothing in the callback throws an exception.
|
||||
MUST(scope_body->for_each_lexically_declared_identifier([&](auto const& identifier) {
|
||||
if (identifier.string() == arguments_name)
|
||||
arguments_object_needed = false;
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
arguments_object_needed = false;
|
||||
}
|
||||
// NOTE: Following steps were executed in ECMAScriptFunctionObject constructor.
|
||||
// 8. Let hasParameterExpressions be ContainsExpression of formals.
|
||||
// 9. Let varNames be the VarDeclaredNames of code.
|
||||
// 10. Let varDeclarations be the VarScopedDeclarations of code.
|
||||
// 11. Let lexicalNames be the LexicallyDeclaredNames of code.
|
||||
// 12. Let functionNames be a new empty List.
|
||||
// 13. Let functionsToInitialize be a new empty List.
|
||||
// 14. For each element d of varDeclarations, in reverse List order, do
|
||||
// 15. Let argumentsObjectNeeded be true.
|
||||
// 16. If func.[[ThisMode]] is lexical, then
|
||||
// 17. Else if parameterNames contains "arguments", then
|
||||
// 18. Else if hasParameterExpressions is false, then
|
||||
|
||||
GCPtr<Environment> environment;
|
||||
|
||||
// 19. If strict is true or hasParameterExpressions is false, then
|
||||
if (strict || !has_parameter_expressions) {
|
||||
if (m_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.
|
||||
environment = callee_context.lexical_environment;
|
||||
|
@ -471,7 +542,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
}
|
||||
|
||||
// 21. For each String paramName of parameterNames, do
|
||||
for (auto const& parameter_name : parameter_names) {
|
||||
for (auto const& parameter_name : m_parameter_names) {
|
||||
// a. Let alreadyDeclared be ! env.HasBinding(paramName).
|
||||
auto already_declared = MUST(environment->has_binding(parameter_name));
|
||||
|
||||
|
@ -483,7 +554,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
MUST(environment->create_mutable_binding(vm, parameter_name, false));
|
||||
|
||||
// ii. If hasDuplicates is true, then
|
||||
if (has_duplicates) {
|
||||
if (m_has_duplicates) {
|
||||
// 1. Perform ! env.InitializeBinding(paramName, undefined).
|
||||
MUST(environment->initialize_binding(vm, parameter_name, js_undefined(), Environment::InitializeBindingHint::Normal));
|
||||
}
|
||||
|
@ -491,11 +562,11 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
}
|
||||
|
||||
// 22. If argumentsObjectNeeded is true, then
|
||||
if (arguments_object_needed) {
|
||||
if (m_arguments_object_needed) {
|
||||
Object* arguments_object;
|
||||
|
||||
// a. If strict is true or simpleParameterList is false, then
|
||||
if (strict || !simple_parameter_list) {
|
||||
if (m_strict || !simple_parameter_list) {
|
||||
// i. Let ao be CreateUnmappedArgumentsObject(argumentsList).
|
||||
arguments_object = create_unmapped_arguments_object(vm, vm.running_execution_context().arguments);
|
||||
}
|
||||
|
@ -508,7 +579,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
}
|
||||
|
||||
// c. If strict is true, then
|
||||
if (strict) {
|
||||
if (m_strict) {
|
||||
// i. Perform ! env.CreateImmutableBinding("arguments", false).
|
||||
MUST(environment->create_immutable_binding(vm, vm.names.arguments.as_string(), false));
|
||||
|
||||
|
@ -524,7 +595,6 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
MUST(environment->initialize_binding(vm, vm.names.arguments.as_string(), arguments_object, Environment::InitializeBindingHint::Normal));
|
||||
|
||||
// f. Let parameterBindings be the list-concatenation of parameterNames and « "arguments" ».
|
||||
parameter_names.set(vm.names.arguments.as_string());
|
||||
}
|
||||
// 23. Else,
|
||||
else {
|
||||
|
@ -567,7 +637,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
argument_value = js_undefined();
|
||||
}
|
||||
|
||||
Environment* used_environment = has_duplicates ? nullptr : environment;
|
||||
Environment* used_environment = m_has_duplicates ? nullptr : environment;
|
||||
|
||||
if constexpr (IsSame<NonnullRefPtr<Identifier const> const&, decltype(param)>) {
|
||||
if (param->is_local()) {
|
||||
|
@ -576,7 +646,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
}
|
||||
Reference reference = TRY(vm.resolve_binding(param->string(), used_environment));
|
||||
// Here the difference from hasDuplicates is important
|
||||
if (has_duplicates)
|
||||
if (m_has_duplicates)
|
||||
return reference.put_value(vm, argument_value);
|
||||
return reference.initialize_referenced_binding(vm, argument_value);
|
||||
}
|
||||
|
@ -594,7 +664,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
instantiated_var_names.ensure_capacity(scope_body->var_declaration_count());
|
||||
|
||||
// 27. If hasParameterExpressions is false, then
|
||||
if (!has_parameter_expressions) {
|
||||
if (!m_has_parameter_expressions) {
|
||||
// a. NOTE: Only a single Environment Record is needed for the parameters and top-level vars.
|
||||
|
||||
// b. Let instantiatedVarNames be a copy of the List parameterBindings.
|
||||
|
@ -605,21 +675,20 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
// an exception should not result from `for_each_var_declared_name`.
|
||||
|
||||
// c. For each element n of varNames, do
|
||||
MUST(scope_body->for_each_var_declared_identifier([&](auto const& id) {
|
||||
// i. If instantiatedVarNames does not contain n, then
|
||||
if (!parameter_names.contains(id.string()) && instantiated_var_names.set(id.string()) == AK::HashSetResult::InsertedNewEntry) {
|
||||
// 1. Append n to instantiatedVarNames.
|
||||
|
||||
for (auto const& variable_to_initialize : m_var_names_to_initialize_binding) {
|
||||
auto const& id = variable_to_initialize.identifier;
|
||||
// NOTE: Following steps were executed in ECMAScriptFunctionObject constructor.
|
||||
// i. If instantiatedVarNames does not contain n, then
|
||||
// 1. Append n to instantiatedVarNames.
|
||||
if (id.is_local()) {
|
||||
callee_context.local_variables[id.local_variable_index()] = js_undefined();
|
||||
} else {
|
||||
// 2. Perform ! env.CreateMutableBinding(n, false).
|
||||
// 3. Perform ! env.InitializeBinding(n, undefined).
|
||||
if (id.is_local()) {
|
||||
callee_context.local_variables[id.local_variable_index()] = js_undefined();
|
||||
} else {
|
||||
MUST(environment->create_mutable_binding(vm, id.string(), false));
|
||||
MUST(environment->initialize_binding(vm, id.string(), js_undefined(), Environment::InitializeBindingHint::Normal));
|
||||
}
|
||||
MUST(environment->create_mutable_binding(vm, id.string(), false));
|
||||
MUST(environment->initialize_binding(vm, id.string(), js_undefined(), Environment::InitializeBindingHint::Normal));
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// d.Let varEnv be env
|
||||
|
@ -643,69 +712,61 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
// an exception should not result from `for_each_var_declared_name`.
|
||||
|
||||
// e. For each element n of varNames, do
|
||||
MUST(scope_body->for_each_var_declared_identifier([&](auto const& id) {
|
||||
// i. If instantiatedVarNames does not contain n, then
|
||||
if (instantiated_var_names.set(id.string()) == AK::HashSetResult::InsertedNewEntry) {
|
||||
// 1. Append n to instantiatedVarNames.
|
||||
for (auto const& variable_to_initialize : m_var_names_to_initialize_binding) {
|
||||
auto const& id = variable_to_initialize.identifier;
|
||||
|
||||
// 2. Perform ! varEnv.CreateMutableBinding(n, false).
|
||||
MUST(var_environment->create_mutable_binding(vm, id.string(), false));
|
||||
// NOTE: Following steps were executed in ECMAScriptFunctionObject constructor.
|
||||
// i. If instantiatedVarNames does not contain n, then
|
||||
// 1. Append n to instantiatedVarNames.
|
||||
|
||||
Value initial_value;
|
||||
// 2. Perform ! varEnv.CreateMutableBinding(n, false).
|
||||
MUST(var_environment->create_mutable_binding(vm, id.string(), false));
|
||||
|
||||
// 3. If parameterBindings does not contain n, or if functionNames contains n, then
|
||||
if (!parameter_names.contains(id.string()) || function_names.contains(id.string())) {
|
||||
// a. Let initialValue be undefined.
|
||||
initial_value = js_undefined();
|
||||
}
|
||||
// 4. Else,
|
||||
else {
|
||||
// a. Let initialValue be ! env.GetBindingValue(n, false).
|
||||
if (id.is_local()) {
|
||||
initial_value = callee_context.local_variables[id.local_variable_index()];
|
||||
} else {
|
||||
initial_value = MUST(environment->get_binding_value(vm, id.string(), false));
|
||||
}
|
||||
}
|
||||
Value initial_value;
|
||||
|
||||
// 5. Perform ! varEnv.InitializeBinding(n, initialValue).
|
||||
if (id.is_local()) {
|
||||
// NOTE: Local variables are supported only in bytecode interpreter
|
||||
callee_context.local_variables[id.local_variable_index()] = initial_value;
|
||||
} else {
|
||||
MUST(var_environment->initialize_binding(vm, id.string(), initial_value, Environment::InitializeBindingHint::Normal));
|
||||
}
|
||||
|
||||
// 6. NOTE: A var with the same name as a formal parameter initially has the same value as the corresponding initialized parameter.
|
||||
// 3. If parameterBindings does not contain n, or if functionNames contains n, then
|
||||
if (!variable_to_initialize.parameter_binding || variable_to_initialize.function_name) {
|
||||
// a. Let initialValue be undefined.
|
||||
initial_value = js_undefined();
|
||||
}
|
||||
}));
|
||||
// 4. Else,
|
||||
else {
|
||||
// a. Let initialValue be ! env.GetBindingValue(n, false).
|
||||
if (id.is_local()) {
|
||||
initial_value = callee_context.local_variables[id.local_variable_index()];
|
||||
} else {
|
||||
initial_value = MUST(environment->get_binding_value(vm, id.string(), false));
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Perform ! varEnv.InitializeBinding(n, initialValue).
|
||||
if (id.is_local()) {
|
||||
// NOTE: Local variables are supported only in bytecode interpreter
|
||||
callee_context.local_variables[id.local_variable_index()] = initial_value;
|
||||
} else {
|
||||
MUST(var_environment->initialize_binding(vm, id.string(), initial_value, Environment::InitializeBindingHint::Normal));
|
||||
}
|
||||
|
||||
// 6. NOTE: A var with the same name as a formal parameter initially has the same value as the corresponding initialized parameter.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (!strict && scope_body) {
|
||||
if (!m_strict && scope_body) {
|
||||
// NOTE: Due to the use of MUST with `create_mutable_binding` and `initialize_binding` below,
|
||||
// an exception should not result from `for_each_function_hoistable_with_annexB_extension`.
|
||||
MUST(scope_body->for_each_function_hoistable_with_annexB_extension([&](FunctionDeclaration& function_declaration) {
|
||||
auto function_name = function_declaration.name();
|
||||
if (parameter_names.contains(function_name))
|
||||
return;
|
||||
// The spec says 'initializedBindings' here but that does not exist and it then adds it to 'instantiatedVarNames' so it probably means 'instantiatedVarNames'.
|
||||
if (!instantiated_var_names.contains(function_name) && function_name != vm.names.arguments.as_string()) {
|
||||
MUST(var_environment->create_mutable_binding(vm, function_name, false));
|
||||
MUST(var_environment->initialize_binding(vm, function_name, js_undefined(), Environment::InitializeBindingHint::Normal));
|
||||
instantiated_var_names.set(function_name);
|
||||
}
|
||||
|
||||
function_declaration.set_should_do_additional_annexB_steps();
|
||||
}));
|
||||
for (auto const& function_name : m_function_names_to_initialize_binding) {
|
||||
MUST(var_environment->create_mutable_binding(vm, function_name, false));
|
||||
MUST(var_environment->initialize_binding(vm, function_name, js_undefined(), Environment::InitializeBindingHint::Normal));
|
||||
}
|
||||
}
|
||||
|
||||
GCPtr<Environment> lex_environment;
|
||||
|
||||
// 30. If strict is false, then
|
||||
if (!strict) {
|
||||
if (!m_strict) {
|
||||
// Optimization: We avoid creating empty top-level declarative environments in non-strict mode, if both of these conditions are true:
|
||||
// 1. there is no direct call to eval() within this function
|
||||
// 2. there are no lexical declarations that would go into the environment
|
||||
|
@ -766,7 +827,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
auto private_environment = callee_context.private_environment;
|
||||
|
||||
// 36. For each Parse Node f of functionsToInitialize, do
|
||||
for (auto& declaration : functions_to_initialize) {
|
||||
for (auto& declaration : m_functions_to_initialize) {
|
||||
// a. Let fn be the sole element of the BoundNames of f.
|
||||
// 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(), lex_environment, private_environment, declaration.kind(), declaration.is_strict_mode(), declaration.might_need_arguments_object(), declaration.contains_direct_call_to_eval());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue