mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
LibJS: Convert ScopeNode declaration functions to ThrowCompletionOr
This removes a number of vm.exception() checks which are now caught directly by TRY. Make use of these checks in {Global, Eval}DeclarationInstantiation and while we're here add spec comments.
This commit is contained in:
parent
cc7a117f0f
commit
4136cbdb09
Notes:
sideshowbarker
2024-07-17 19:38:25 +09:00
Author: https://github.com/davidot Commit: https://github.com/SerenityOS/serenity/commit/4136cbdb091 Pull-request: https://github.com/SerenityOS/serenity/pull/12343 Reviewed-by: https://github.com/linusg
6 changed files with 462 additions and 305 deletions
|
@ -256,10 +256,21 @@ Completion FunctionDeclaration::execute(Interpreter& interpreter, GlobalObject&
|
|||
|
||||
if (m_is_hoisted) {
|
||||
// Perform special annexB steps see step 3 of: https://tc39.es/ecma262/#sec-web-compat-functiondeclarationinstantiation
|
||||
|
||||
// i. Let genv be the running execution context's VariableEnvironment.
|
||||
auto* variable_environment = interpreter.vm().running_execution_context().variable_environment;
|
||||
|
||||
// ii. Let benv be the running execution context's LexicalEnvironment.
|
||||
auto* lexical_environment = interpreter.vm().running_execution_context().lexical_environment;
|
||||
|
||||
// iii. Let fobj be ! benv.GetBindingValue(F, false).
|
||||
auto function_object = MUST(lexical_environment->get_binding_value(global_object, name(), false));
|
||||
MUST(variable_environment->set_mutable_binding(global_object, name(), function_object, false));
|
||||
|
||||
// iv. Perform ? genv.SetMutableBinding(F, fobj, false).
|
||||
TRY(variable_environment->set_mutable_binding(global_object, name(), function_object, false));
|
||||
|
||||
// v. Return NormalCompletion(empty).
|
||||
return normal_completion({});
|
||||
}
|
||||
|
||||
// 1. Return NormalCompletion(empty).
|
||||
|
@ -2113,10 +2124,12 @@ void ClassDeclaration::dump(int indent) const
|
|||
m_class_expression->dump(indent + 1);
|
||||
}
|
||||
|
||||
void ClassDeclaration::for_each_bound_name(IteratorOrVoidFunction<FlyString const&> callback) const
|
||||
ThrowCompletionOr<void> ClassDeclaration::for_each_bound_name(ThrowCompletionOrVoidCallback<FlyString const&>&& callback) const
|
||||
{
|
||||
if (!m_class_expression->name().is_empty())
|
||||
callback(m_class_expression->name());
|
||||
if (m_class_expression->name().is_empty())
|
||||
return {};
|
||||
|
||||
return callback(m_class_expression->name());
|
||||
}
|
||||
|
||||
void ClassExpression::dump(int indent) const
|
||||
|
@ -2241,6 +2254,23 @@ bool BindingPattern::contains_expression() const
|
|||
return false;
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> BindingPattern::for_each_bound_name(ThrowCompletionOrVoidCallback<FlyString const&>&& callback) const
|
||||
{
|
||||
for (auto const& entry : entries) {
|
||||
auto const& alias = entry.alias;
|
||||
if (alias.has<NonnullRefPtr<Identifier>>()) {
|
||||
TRY(callback(alias.get<NonnullRefPtr<Identifier>>()->string()));
|
||||
} else if (alias.has<NonnullRefPtr<BindingPattern>>()) {
|
||||
TRY(alias.get<NonnullRefPtr<BindingPattern>>()->for_each_bound_name(forward<decltype(callback)>(callback)));
|
||||
} else {
|
||||
auto const& name = entry.name;
|
||||
if (name.has<NonnullRefPtr<Identifier>>())
|
||||
TRY(callback(name.get<NonnullRefPtr<Identifier>>()->string()));
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void BindingPattern::dump(int indent) const
|
||||
{
|
||||
print_indent(indent);
|
||||
|
@ -2324,10 +2354,11 @@ void FunctionDeclaration::dump(int indent) const
|
|||
FunctionNode::dump(indent, class_name());
|
||||
}
|
||||
|
||||
void FunctionDeclaration::for_each_bound_name(IteratorOrVoidFunction<FlyString const&> callback) const
|
||||
ThrowCompletionOr<void> FunctionDeclaration::for_each_bound_name(ThrowCompletionOrVoidCallback<const FlyString&>&& callback) const
|
||||
{
|
||||
if (!name().is_empty())
|
||||
callback(name());
|
||||
if (name().is_empty())
|
||||
return {};
|
||||
return callback(name());
|
||||
}
|
||||
|
||||
void FunctionExpression::dump(int indent) const
|
||||
|
@ -2861,19 +2892,21 @@ Completion VariableDeclarator::execute(Interpreter& interpreter, GlobalObject&)
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
void VariableDeclaration::for_each_bound_name(IteratorOrVoidFunction<FlyString const&> callback) const
|
||||
ThrowCompletionOr<void> VariableDeclaration::for_each_bound_name(ThrowCompletionOrVoidCallback<FlyString const&>&& callback) const
|
||||
{
|
||||
for (auto& entry : declarations()) {
|
||||
entry.target().template visit(
|
||||
[&](const NonnullRefPtr<Identifier>& id) {
|
||||
callback(id->string());
|
||||
for (auto const& entry : declarations()) {
|
||||
TRY(entry.target().visit(
|
||||
[&](NonnullRefPtr<Identifier> const& id) {
|
||||
return callback(id->string());
|
||||
},
|
||||
[&](const NonnullRefPtr<BindingPattern>& binding) {
|
||||
binding->for_each_bound_name([&](const auto& name) {
|
||||
callback(name);
|
||||
[&](NonnullRefPtr<BindingPattern> const& binding) {
|
||||
return binding->for_each_bound_name([&](auto const& name) {
|
||||
return callback(name);
|
||||
});
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void VariableDeclaration::dump(int indent) const
|
||||
|
@ -4097,76 +4130,62 @@ Completion DebuggerStatement::execute(Interpreter& interpreter, GlobalObject&) c
|
|||
// 3. Return result.
|
||||
return result;
|
||||
}
|
||||
|
||||
void ScopeNode::for_each_lexically_scoped_declaration(IteratorOrVoidFunction<Declaration const&>&& callback) const
|
||||
ThrowCompletionOr<void> ScopeNode::for_each_lexically_scoped_declaration(ThrowCompletionOrVoidCallback<Declaration const&>&& callback) const
|
||||
{
|
||||
for (auto& declaration : m_lexical_declarations) {
|
||||
if (callback(declaration) == IterationDecision::Break)
|
||||
break;
|
||||
}
|
||||
for (auto& declaration : m_lexical_declarations)
|
||||
TRY(callback(declaration));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void ScopeNode::for_each_lexically_declared_name(IteratorOrVoidFunction<FlyString const&>&& callback) const
|
||||
ThrowCompletionOr<void> ScopeNode::for_each_lexically_declared_name(ThrowCompletionOrVoidCallback<FlyString const&>&& callback) const
|
||||
{
|
||||
auto running = true;
|
||||
for (auto& declaration : m_lexical_declarations) {
|
||||
declaration.for_each_bound_name([&](auto const& name) {
|
||||
if (callback(name) == IterationDecision::Break) {
|
||||
running = false;
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
if (!running)
|
||||
break;
|
||||
for (auto const& declaration : m_lexical_declarations) {
|
||||
TRY(declaration.for_each_bound_name([&](auto const& name) {
|
||||
return callback(name);
|
||||
}));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void ScopeNode::for_each_var_declared_name(IteratorOrVoidFunction<FlyString const&>&& callback) const
|
||||
ThrowCompletionOr<void> ScopeNode::for_each_var_declared_name(ThrowCompletionOrVoidCallback<FlyString const&>&& callback) const
|
||||
{
|
||||
auto running = true;
|
||||
for (auto& declaration : m_var_declarations) {
|
||||
declaration.for_each_bound_name([&](auto const& name) {
|
||||
if (callback(name) == IterationDecision::Break) {
|
||||
running = false;
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
if (!running)
|
||||
break;
|
||||
TRY(declaration.for_each_bound_name([&](auto const& name) {
|
||||
return callback(name);
|
||||
}));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void ScopeNode::for_each_var_function_declaration_in_reverse_order(IteratorOrVoidFunction<FunctionDeclaration const&>&& callback) const
|
||||
ThrowCompletionOr<void> ScopeNode::for_each_var_function_declaration_in_reverse_order(ThrowCompletionOrVoidCallback<FunctionDeclaration const&>&& callback) const
|
||||
{
|
||||
for (ssize_t i = m_var_declarations.size() - 1; i >= 0; i--) {
|
||||
auto& declaration = m_var_declarations[i];
|
||||
if (is<FunctionDeclaration>(declaration)) {
|
||||
if (callback(static_cast<FunctionDeclaration const&>(declaration)) == IterationDecision::Break)
|
||||
break;
|
||||
}
|
||||
if (is<FunctionDeclaration>(declaration))
|
||||
TRY(callback(static_cast<FunctionDeclaration const&>(declaration)));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void ScopeNode::for_each_var_scoped_variable_declaration(IteratorOrVoidFunction<VariableDeclaration const&>&& callback) const
|
||||
ThrowCompletionOr<void> ScopeNode::for_each_var_scoped_variable_declaration(ThrowCompletionOrVoidCallback<VariableDeclaration const&>&& callback) const
|
||||
{
|
||||
for (auto& declaration : m_var_declarations) {
|
||||
if (!is<FunctionDeclaration>(declaration)) {
|
||||
VERIFY(is<VariableDeclaration>(declaration));
|
||||
if (callback(static_cast<VariableDeclaration const&>(declaration)) == IterationDecision::Break)
|
||||
break;
|
||||
TRY(callback(static_cast<VariableDeclaration const&>(declaration)));
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void ScopeNode::for_each_function_hoistable_with_annexB_extension(IteratorOrVoidFunction<FunctionDeclaration&>&& callback) const
|
||||
ThrowCompletionOr<void> ScopeNode::for_each_function_hoistable_with_annexB_extension(ThrowCompletionOrVoidCallback<FunctionDeclaration&>&& callback) const
|
||||
{
|
||||
for (auto& function : m_functions_hoistable_with_annexB_extension) {
|
||||
// We need const_cast here since it might have to set a property on function declaration.
|
||||
if (callback(const_cast<FunctionDeclaration&>(function)) == IterationDecision::Break)
|
||||
break;
|
||||
TRY(callback(const_cast<FunctionDeclaration&>(function)));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void ScopeNode::add_lexical_declaration(NonnullRefPtr<Declaration> declaration)
|
||||
|
@ -4362,6 +4381,7 @@ void ScopeNode::block_declaration_instantiation(GlobalObject& global_object, Env
|
|||
// See also B.3.2.6 Changes to BlockDeclarationInstantiation, https://tc39.es/ecma262/#sec-web-compat-blockdeclarationinstantiation
|
||||
VERIFY(environment);
|
||||
auto* private_environment = global_object.vm().running_execution_context().private_environment;
|
||||
// Note: All the calls here are ! and thus we do not need to TRY this callback.
|
||||
for_each_lexically_scoped_declaration([&](Declaration const& declaration) {
|
||||
auto is_constant_declaration = declaration.is_constant_declaration();
|
||||
declaration.for_each_bound_name([&](auto const& name) {
|
||||
|
@ -4385,150 +4405,196 @@ void ScopeNode::block_declaration_instantiation(GlobalObject& global_object, Env
|
|||
// 16.1.7 GlobalDeclarationInstantiation ( script, env ), https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
|
||||
ThrowCompletionOr<void> Program::global_declaration_instantiation(Interpreter& interpreter, GlobalObject& global_object, GlobalEnvironment& global_environment) const
|
||||
{
|
||||
for_each_lexically_declared_name([&](FlyString const& name) {
|
||||
if (global_environment.has_var_declaration(name) || global_environment.has_lexical_declaration(name)) {
|
||||
interpreter.vm().throw_exception<SyntaxError>(global_object, ErrorType::TopLevelVariableAlreadyDeclared, name);
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
// 1. Let lexNames be the LexicallyDeclaredNames of script.
|
||||
// 2. Let varNames be the VarDeclaredNames of script.
|
||||
// 3. For each element name of lexNames, do
|
||||
TRY(for_each_lexically_declared_name([&](FlyString const& name) -> ThrowCompletionOr<void> {
|
||||
// a. If env.HasVarDeclaration(name) is true, throw a SyntaxError exception.
|
||||
if (global_environment.has_var_declaration(name))
|
||||
return interpreter.vm().throw_completion<SyntaxError>(global_object, ErrorType::TopLevelVariableAlreadyDeclared, name);
|
||||
|
||||
auto restricted_global_or_error = global_environment.has_restricted_global_property(name);
|
||||
if (restricted_global_or_error.is_error())
|
||||
return IterationDecision::Break;
|
||||
auto restricted_global = restricted_global_or_error.release_value();
|
||||
// b. If env.HasLexicalDeclaration(name) is true, throw a SyntaxError exception.
|
||||
if (global_environment.has_lexical_declaration(name))
|
||||
return interpreter.vm().throw_completion<SyntaxError>(global_object, ErrorType::TopLevelVariableAlreadyDeclared, name);
|
||||
|
||||
if (restricted_global)
|
||||
interpreter.vm().throw_exception<SyntaxError>(global_object, ErrorType::RestrictedGlobalProperty, name);
|
||||
// c. Let hasRestrictedGlobal be ? env.HasRestrictedGlobalProperty(name).
|
||||
auto has_restricted_global = TRY(global_environment.has_restricted_global_property(name));
|
||||
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
// d. If hasRestrictedGlobal is true, throw a SyntaxError exception.
|
||||
if (has_restricted_global)
|
||||
return interpreter.vm().throw_completion<SyntaxError>(global_object, ErrorType::RestrictedGlobalProperty, name);
|
||||
|
||||
if (auto* exception = interpreter.exception())
|
||||
return throw_completion(exception->value());
|
||||
return {};
|
||||
}));
|
||||
|
||||
for_each_var_declared_name([&](auto const& name) {
|
||||
if (global_environment.has_lexical_declaration(name)) {
|
||||
interpreter.vm().throw_exception<SyntaxError>(global_object, ErrorType::TopLevelVariableAlreadyDeclared, name);
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
// 4. For each element name of varNames, do
|
||||
TRY(for_each_var_declared_name([&](auto const& name) -> ThrowCompletionOr<void> {
|
||||
// a. If env.HasLexicalDeclaration(name) is true, throw a SyntaxError exception.
|
||||
if (global_environment.has_lexical_declaration(name))
|
||||
return interpreter.vm().throw_completion<SyntaxError>(global_object, ErrorType::TopLevelVariableAlreadyDeclared, name);
|
||||
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
return {};
|
||||
}));
|
||||
|
||||
if (auto* exception = interpreter.exception())
|
||||
return throw_completion(exception->value());
|
||||
|
||||
HashTable<FlyString> declared_function_names;
|
||||
// 5. Let varDeclarations be the VarScopedDeclarations of script.
|
||||
// 6. Let functionsToInitialize be a new empty List.
|
||||
Vector<FunctionDeclaration const&> functions_to_initialize;
|
||||
|
||||
for_each_var_function_declaration_in_reverse_order([&](FunctionDeclaration const& function) {
|
||||
// 7. Let declaredFunctionNames be a new empty List.
|
||||
HashTable<FlyString> declared_function_names;
|
||||
|
||||
// 8. For each element d of varDeclarations, in reverse List order, do
|
||||
|
||||
TRY(for_each_var_function_declaration_in_reverse_order([&](FunctionDeclaration const& function) -> ThrowCompletionOr<void> {
|
||||
// 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.
|
||||
// Note: This is checked in for_each_var_function_declaration_in_reverse_order.
|
||||
|
||||
// ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used.
|
||||
|
||||
// iii. Let fn be the sole element of the BoundNames of d.
|
||||
|
||||
// iv. If fn is not an element of declaredFunctionNames, then
|
||||
if (declared_function_names.set(function.name()) != AK::HashSetResult::InsertedNewEntry)
|
||||
return IterationDecision::Continue;
|
||||
return {};
|
||||
|
||||
auto function_definable_or_error = global_environment.can_declare_global_function(function.name());
|
||||
if (function_definable_or_error.is_error())
|
||||
return IterationDecision::Break;
|
||||
auto function_definable = function_definable_or_error.release_value();
|
||||
// 1. Let fnDefinable be ? env.CanDeclareGlobalFunction(fn).
|
||||
auto function_definable = TRY(global_environment.can_declare_global_function(function.name()));
|
||||
|
||||
if (!function_definable) {
|
||||
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::CannotDeclareGlobalFunction, function.name());
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
// 2. If fnDefinable is false, throw a TypeError exception.
|
||||
if (!function_definable)
|
||||
return interpreter.vm().throw_completion<TypeError>(global_object, ErrorType::CannotDeclareGlobalFunction, function.name());
|
||||
|
||||
// 3. Append fn to declaredFunctionNames.
|
||||
// Note: Already done in step iv. above.
|
||||
|
||||
// 4. Insert d as the first element of functionsToInitialize.
|
||||
functions_to_initialize.append(function);
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
if (auto* exception = interpreter.exception())
|
||||
return throw_completion(exception->value());
|
||||
return {};
|
||||
}));
|
||||
|
||||
// 9. Let declaredVarNames be a new empty List.
|
||||
HashTable<FlyString> declared_var_names;
|
||||
|
||||
for_each_var_scoped_variable_declaration([&](Declaration const& declaration) {
|
||||
declaration.for_each_bound_name([&](auto const& name) {
|
||||
// 10. For each element d of varDeclarations, do
|
||||
TRY(for_each_var_scoped_variable_declaration([&](Declaration const& declaration) {
|
||||
// a. If d is a VariableDeclaration, a ForBinding, or a BindingIdentifier, then
|
||||
// Note: This is done in for_each_var_scoped_variable_declaration.
|
||||
|
||||
// i. For each String vn of the BoundNames of d, do
|
||||
return declaration.for_each_bound_name([&](auto const& name) -> ThrowCompletionOr<void> {
|
||||
// 1. If vn is not an element of declaredFunctionNames, then
|
||||
if (declared_function_names.contains(name))
|
||||
return IterationDecision::Continue;
|
||||
return {};
|
||||
|
||||
auto var_definable_or_error = global_environment.can_declare_global_var(name);
|
||||
if (var_definable_or_error.is_error())
|
||||
return IterationDecision::Break;
|
||||
auto var_definable = var_definable_or_error.release_value();
|
||||
// a. Let vnDefinable be ? env.CanDeclareGlobalVar(vn).
|
||||
auto var_definable = TRY(global_environment.can_declare_global_var(name));
|
||||
|
||||
if (!var_definable) {
|
||||
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::CannotDeclareGlobalVariable, name);
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
// b. If vnDefinable is false, throw a TypeError exception.
|
||||
if (!var_definable)
|
||||
return interpreter.vm().throw_completion<TypeError>(global_object, ErrorType::CannotDeclareGlobalVariable, name);
|
||||
|
||||
// c. If vn is not an element of declaredVarNames, then
|
||||
// i. Append vn to declaredVarNames.
|
||||
declared_var_names.set(name);
|
||||
return IterationDecision::Continue;
|
||||
return {};
|
||||
});
|
||||
if (interpreter.exception())
|
||||
return IterationDecision::Break;
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}));
|
||||
|
||||
if (auto* exception = interpreter.exception())
|
||||
return throw_completion(exception->value());
|
||||
// 11. NOTE: No abnormal terminations occur after this algorithm step if the global object is an ordinary object. However, if the global object is a Proxy exotic object it may exhibit behaviours that cause abnormal terminations in some of the following steps.
|
||||
// 12. NOTE: Annex B.3.2.2 adds additional steps at this point.
|
||||
|
||||
// 12. Let strict be IsStrict of script.
|
||||
// 13. If strict is false, then
|
||||
if (!m_is_strict_mode) {
|
||||
for_each_function_hoistable_with_annexB_extension([&](FunctionDeclaration& function_declaration) {
|
||||
// a. Let declaredFunctionOrVarNames be the list-concatenation of declaredFunctionNames and declaredVarNames.
|
||||
// b. For each FunctionDeclaration f that is directly contained in the StatementList of a Block, CaseClause, or DefaultClause Contained within script, do
|
||||
TRY(for_each_function_hoistable_with_annexB_extension([&](FunctionDeclaration& function_declaration) -> ThrowCompletionOr<void> {
|
||||
// i. Let F be StringValue of the BindingIdentifier of f.
|
||||
auto& function_name = function_declaration.name();
|
||||
|
||||
// ii. If replacing the FunctionDeclaration f with a VariableStatement that has F as a BindingIdentifier would not produce any Early Errors for script, then
|
||||
// Note: This step is already performed during parsing and for_each_function_hoistable_with_annexB_extension so this always passes here.
|
||||
|
||||
// 1. If env.HasLexicalDeclaration(F) is false, then
|
||||
if (global_environment.has_lexical_declaration(function_name))
|
||||
return IterationDecision::Continue;
|
||||
return {};
|
||||
|
||||
auto function_definable_or_error = global_environment.can_declare_global_function(function_name);
|
||||
if (function_definable_or_error.is_error())
|
||||
return IterationDecision::Break;
|
||||
auto function_definable = function_definable_or_error.release_value();
|
||||
// a. Let fnDefinable be ? env.CanDeclareGlobalVar(F).
|
||||
auto function_definable = TRY(global_environment.can_declare_global_function(function_name));
|
||||
// b. If fnDefinable is true, then
|
||||
|
||||
if (!function_definable) {
|
||||
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::CannotDeclareGlobalFunction, function_name);
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
if (!function_definable)
|
||||
return {};
|
||||
|
||||
// i. NOTE: A var binding for F is only instantiated here if it is neither a VarDeclaredName nor the name of another FunctionDeclaration.
|
||||
|
||||
// ii. If declaredFunctionOrVarNames does not contain F, then
|
||||
|
||||
if (!declared_function_names.contains(function_name) && !declared_var_names.contains(function_name)) {
|
||||
auto result = global_environment.create_global_var_binding(function_name, false);
|
||||
if (result.is_error())
|
||||
return IterationDecision::Break;
|
||||
// i. Perform ? env.CreateGlobalVarBinding(F, false).
|
||||
TRY(global_environment.create_global_var_binding(function_name, false));
|
||||
|
||||
// ii. Append F to declaredFunctionOrVarNames.
|
||||
declared_function_names.set(function_name);
|
||||
}
|
||||
|
||||
// iii. When the FunctionDeclaration f is evaluated, perform the following steps in place of the FunctionDeclaration Evaluation algorithm provided in 15.2.6:
|
||||
// i. Let genv be the running execution context's VariableEnvironment.
|
||||
// ii. Let benv be the running execution context's LexicalEnvironment.
|
||||
// iii. Let fobj be ! benv.GetBindingValue(F, false).
|
||||
// iv. Perform ? genv.SetMutableBinding(F, fobj, false).
|
||||
// v. Return NormalCompletion(empty).
|
||||
function_declaration.set_should_do_additional_annexB_steps();
|
||||
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
if (auto* exception = interpreter.exception())
|
||||
return throw_completion(exception->value());
|
||||
return {};
|
||||
}));
|
||||
|
||||
// We should not use declared function names below here anymore since these functions are not in there in the spec.
|
||||
declared_function_names.clear();
|
||||
}
|
||||
|
||||
// 13. Let lexDeclarations be the LexicallyScopedDeclarations of script.
|
||||
// 14. Let privateEnv be null.
|
||||
PrivateEnvironment* private_environment = nullptr;
|
||||
|
||||
for_each_lexically_scoped_declaration([&](Declaration const& declaration) {
|
||||
declaration.for_each_bound_name([&](auto const& name) {
|
||||
if (declaration.is_constant_declaration())
|
||||
(void)global_environment.create_immutable_binding(global_object, name, true);
|
||||
else
|
||||
(void)global_environment.create_mutable_binding(global_object, name, false);
|
||||
if (interpreter.exception())
|
||||
return IterationDecision::Break;
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
if (interpreter.exception())
|
||||
return IterationDecision::Break;
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
// 15. For each element d of lexDeclarations, do
|
||||
TRY(for_each_lexically_scoped_declaration([&](Declaration const& declaration) {
|
||||
// a. NOTE: Lexically declared names are only instantiated here but not initialized.
|
||||
// b. For each element dn of the BoundNames of d, do
|
||||
return declaration.for_each_bound_name([&](auto const& name) -> ThrowCompletionOr<void> {
|
||||
// i. If IsConstantDeclaration of d is true, then
|
||||
if (declaration.is_constant_declaration()) {
|
||||
// 1. Perform ? env.CreateImmutableBinding(dn, true).
|
||||
TRY(global_environment.create_immutable_binding(global_object, name, true));
|
||||
}
|
||||
// ii. Else,
|
||||
else {
|
||||
// 1. Perform ? env.CreateMutableBinding(dn, false).
|
||||
TRY(global_environment.create_mutable_binding(global_object, name, false));
|
||||
}
|
||||
|
||||
return {};
|
||||
});
|
||||
}));
|
||||
|
||||
// 16. For each Parse Node f of functionsToInitialize, do
|
||||
for (auto& declaration : functions_to_initialize) {
|
||||
// a. Let fn be the sole element of the BoundNames of f.
|
||||
// b. Let fo be InstantiateFunctionObject of f with arguments env and privateEnv.
|
||||
auto* function = ECMAScriptFunctionObject::create(global_object, declaration.name(), declaration.source_text(), declaration.body(), declaration.parameters(), declaration.function_length(), &global_environment, private_environment, declaration.kind(), declaration.is_strict_mode(), declaration.might_need_arguments_object(), declaration.contains_direct_call_to_eval());
|
||||
|
||||
// c. Perform ? env.CreateGlobalFunctionBinding(fn, fo, false).
|
||||
TRY(global_environment.create_global_function_binding(declaration.name(), function, false));
|
||||
}
|
||||
|
||||
for (auto& var_name : declared_var_names)
|
||||
// 17. For each String vn of declaredVarNames, do
|
||||
for (auto& var_name : declared_var_names) {
|
||||
// a. Perform ? env.CreateGlobalVarBinding(vn, false).
|
||||
TRY(global_environment.create_global_var_binding(var_name, false));
|
||||
}
|
||||
|
||||
// 18. Return NormalCompletion(empty).
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -167,21 +167,30 @@ private:
|
|||
NonnullRefPtr<Expression> m_expression;
|
||||
};
|
||||
|
||||
template<typename Func, typename... Args>
|
||||
concept ThrowCompletionOrVoidFunction = requires(Func func, Args... args)
|
||||
{
|
||||
{
|
||||
func(args...)
|
||||
}
|
||||
->SameAs<ThrowCompletionOr<void>>;
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
class IteratorOrVoidFunction : public Function<IterationDecision(Args...)> {
|
||||
class ThrowCompletionOrVoidCallback : public Function<ThrowCompletionOr<void>(Args...)> {
|
||||
public:
|
||||
template<typename CallableType>
|
||||
IteratorOrVoidFunction(CallableType&& callable) requires(VoidFunction<CallableType, Args...>)
|
||||
: Function<IterationDecision(Args...)>([callable = forward<CallableType>(callable)](Args... args) {
|
||||
ThrowCompletionOrVoidCallback(CallableType&& callable) requires(VoidFunction<CallableType, Args...>)
|
||||
: Function<ThrowCompletionOr<void>(Args...)>([callable = forward<CallableType>(callable)](Args... args) {
|
||||
callable(args...);
|
||||
return IterationDecision::Continue;
|
||||
return ThrowCompletionOr<void> {};
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
template<typename CallableType>
|
||||
IteratorOrVoidFunction(CallableType&& callable) requires(IteratorFunction<CallableType, Args...>)
|
||||
: Function<IterationDecision(Args...)>(forward<CallableType>(callable))
|
||||
ThrowCompletionOrVoidCallback(CallableType&& callable) requires(ThrowCompletionOrVoidFunction<CallableType, Args...>)
|
||||
: Function<ThrowCompletionOr<void>(Args...)>(forward<CallableType>(callable))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
@ -216,17 +225,17 @@ public:
|
|||
[[nodiscard]] size_t var_declaration_count() const { return m_var_declarations.size(); }
|
||||
[[nodiscard]] size_t lexical_declaration_count() const { return m_lexical_declarations.size(); }
|
||||
|
||||
void for_each_lexically_scoped_declaration(IteratorOrVoidFunction<Declaration const&>&& callback) const;
|
||||
void for_each_lexically_declared_name(IteratorOrVoidFunction<FlyString const&>&& callback) const;
|
||||
ThrowCompletionOr<void> for_each_lexically_scoped_declaration(ThrowCompletionOrVoidCallback<Declaration const&>&& callback) const;
|
||||
ThrowCompletionOr<void> for_each_lexically_declared_name(ThrowCompletionOrVoidCallback<FlyString const&>&& callback) const;
|
||||
|
||||
void for_each_var_declared_name(IteratorOrVoidFunction<FlyString const&>&& callback) const;
|
||||
ThrowCompletionOr<void> for_each_var_declared_name(ThrowCompletionOrVoidCallback<FlyString const&>&& callback) const;
|
||||
|
||||
void for_each_var_function_declaration_in_reverse_order(IteratorOrVoidFunction<FunctionDeclaration const&>&& callback) const;
|
||||
void for_each_var_scoped_variable_declaration(IteratorOrVoidFunction<VariableDeclaration const&>&& callback) const;
|
||||
ThrowCompletionOr<void> for_each_var_function_declaration_in_reverse_order(ThrowCompletionOrVoidCallback<FunctionDeclaration const&>&& callback) const;
|
||||
ThrowCompletionOr<void> for_each_var_scoped_variable_declaration(ThrowCompletionOrVoidCallback<VariableDeclaration const&>&& callback) const;
|
||||
|
||||
void block_declaration_instantiation(GlobalObject& global_object, Environment* environment) const;
|
||||
|
||||
void for_each_function_hoistable_with_annexB_extension(IteratorOrVoidFunction<FunctionDeclaration&>&& callback) const;
|
||||
ThrowCompletionOr<void> for_each_function_hoistable_with_annexB_extension(ThrowCompletionOrVoidCallback<FunctionDeclaration&>&& callback) const;
|
||||
|
||||
protected:
|
||||
explicit ScopeNode(SourceRange source_range)
|
||||
|
@ -519,7 +528,7 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
virtual void for_each_bound_name(IteratorOrVoidFunction<FlyString const&> callback) const = 0;
|
||||
virtual ThrowCompletionOr<void> for_each_bound_name(ThrowCompletionOrVoidCallback<FlyString const&>&& callback) const = 0;
|
||||
|
||||
// 8.1.3 Static Semantics: IsConstantDeclaration, https://tc39.es/ecma262/#sec-static-semantics-isconstantdeclaration
|
||||
virtual bool is_constant_declaration() const { return false; }
|
||||
|
@ -535,7 +544,7 @@ public:
|
|||
}
|
||||
Completion execute(Interpreter&, GlobalObject&) const override { return {}; }
|
||||
|
||||
void for_each_bound_name(IteratorOrVoidFunction<FlyString const&>) const override
|
||||
ThrowCompletionOr<void> for_each_bound_name(ThrowCompletionOrVoidCallback<FlyString const&>&&) const override
|
||||
{
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
@ -560,8 +569,7 @@ struct BindingPattern : RefCounted<BindingPattern> {
|
|||
|
||||
void dump(int indent) const;
|
||||
|
||||
template<typename C>
|
||||
void for_each_bound_name(C&& callback) const;
|
||||
ThrowCompletionOr<void> for_each_bound_name(ThrowCompletionOrVoidCallback<FlyString const&>&& callback) const;
|
||||
|
||||
bool contains_expression() const;
|
||||
|
||||
|
@ -643,7 +651,7 @@ public:
|
|||
virtual void dump(int indent) const override;
|
||||
virtual void generate_bytecode(Bytecode::Generator&) const override;
|
||||
|
||||
void for_each_bound_name(IteratorOrVoidFunction<FlyString const&> callback) const override;
|
||||
virtual ThrowCompletionOr<void> for_each_bound_name(ThrowCompletionOrVoidCallback<FlyString const&>&& callback) const override;
|
||||
|
||||
virtual bool is_function_declaration() const override { return true; }
|
||||
|
||||
|
@ -1372,7 +1380,7 @@ public:
|
|||
virtual void dump(int indent) const override;
|
||||
virtual void generate_bytecode(Bytecode::Generator&) const override;
|
||||
|
||||
void for_each_bound_name(IteratorOrVoidFunction<FlyString const&> callback) const override;
|
||||
virtual ThrowCompletionOr<void> for_each_bound_name(ThrowCompletionOrVoidCallback<FlyString const&>&& callback) const override;
|
||||
|
||||
virtual bool is_lexical_declaration() const override { return true; }
|
||||
|
||||
|
@ -1599,7 +1607,7 @@ public:
|
|||
|
||||
NonnullRefPtrVector<VariableDeclarator> const& declarations() const { return m_declarations; }
|
||||
|
||||
virtual void for_each_bound_name(IteratorOrVoidFunction<FlyString const&> callback) const override;
|
||||
virtual ThrowCompletionOr<void> for_each_bound_name(ThrowCompletionOrVoidCallback<FlyString const&>&& callback) const override;
|
||||
|
||||
virtual bool is_constant_declaration() const override { return m_declaration_kind == DeclarationKind::Const; };
|
||||
|
||||
|
@ -2035,23 +2043,6 @@ private:
|
|||
Value m_value;
|
||||
};
|
||||
|
||||
template<typename C>
|
||||
void BindingPattern::for_each_bound_name(C&& callback) const
|
||||
{
|
||||
for (auto& entry : entries) {
|
||||
auto& alias = entry.alias;
|
||||
if (alias.has<NonnullRefPtr<Identifier>>()) {
|
||||
callback(alias.get<NonnullRefPtr<Identifier>>()->string());
|
||||
} else if (alias.has<NonnullRefPtr<BindingPattern>>()) {
|
||||
alias.get<NonnullRefPtr<BindingPattern>>()->for_each_bound_name(forward<C>(callback));
|
||||
} else {
|
||||
auto& name = entry.name;
|
||||
if (name.has<NonnullRefPtr<Identifier>>())
|
||||
callback(name.get<NonnullRefPtr<Identifier>>()->string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool ASTNode::fast_is<NewExpression>() const { return is_new_expression(); }
|
||||
|
||||
|
|
|
@ -36,12 +36,10 @@ void ScopeNode::generate_bytecode(Bytecode::Generator& generator) const
|
|||
HashTable<FlyString> functions_initialized;
|
||||
for_each_var_function_declaration_in_reverse_order([&](FunctionDeclaration const& function) {
|
||||
if (functions_initialized.set(function.name()) != AK::HashSetResult::InsertedNewEntry)
|
||||
return IterationDecision::Continue;
|
||||
return;
|
||||
|
||||
generator.emit<Bytecode::Op::NewFunction>(function);
|
||||
generator.emit<Bytecode::Op::SetVariable>(generator.intern_identifier(function.name()));
|
||||
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
// FIXME: Register lexical and variable scope declarations
|
||||
|
|
|
@ -550,20 +550,14 @@ void Parser::parse_module(Program& program)
|
|||
auto const& exported_name = entry.local_or_import_name;
|
||||
bool found = false;
|
||||
program.for_each_lexically_declared_name([&](auto const& name) {
|
||||
if (name == exported_name) {
|
||||
if (name == exported_name)
|
||||
found = true;
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
if (found)
|
||||
continue;
|
||||
program.for_each_var_declared_name([&](auto const& name) {
|
||||
if (name == exported_name) {
|
||||
if (name == exported_name)
|
||||
found = true;
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
if (!found)
|
||||
syntax_error(String::formatted("'{}' is not declared", exported_name));
|
||||
|
|
|
@ -598,195 +598,310 @@ ThrowCompletionOr<Value> perform_eval(Value x, GlobalObject& caller_realm, Calle
|
|||
// 19.2.1.3 EvalDeclarationInstantiation ( body, varEnv, lexEnv, privateEnv, strict ), https://tc39.es/ecma262/#sec-evaldeclarationinstantiation
|
||||
ThrowCompletionOr<void> eval_declaration_instantiation(VM& vm, GlobalObject& global_object, Program const& program, Environment* variable_environment, Environment* lexical_environment, PrivateEnvironment* private_environment, bool strict)
|
||||
{
|
||||
// FIXME: I'm not sure if the global object is correct here. And this is quite a crucial spot!
|
||||
GlobalEnvironment* global_var_environment = variable_environment->is_global_environment() ? static_cast<GlobalEnvironment*>(variable_environment) : nullptr;
|
||||
|
||||
// 1. Let varNames be the VarDeclaredNames of body.
|
||||
// 2. Let varDeclarations be the VarScopedDeclarations of body.
|
||||
// 3. If strict is false, then
|
||||
if (!strict) {
|
||||
// a. If varEnv is a global Environment Record, then
|
||||
if (global_var_environment) {
|
||||
program.for_each_var_declared_name([&](auto const& name) {
|
||||
if (global_var_environment->has_lexical_declaration(name)) {
|
||||
vm.throw_exception<SyntaxError>(global_object, ErrorType::TopLevelVariableAlreadyDeclared, name);
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
// i. For each element name of varNames, do
|
||||
TRY(program.for_each_var_declared_name([&](auto const& name) -> ThrowCompletionOr<void> {
|
||||
// 1. If varEnv.HasLexicalDeclaration(name) is true, throw a SyntaxError exception.
|
||||
if (global_var_environment->has_lexical_declaration(name))
|
||||
return vm.throw_completion<SyntaxError>(global_object, ErrorType::TopLevelVariableAlreadyDeclared, name);
|
||||
|
||||
// 2. NOTE: eval will not create a global var declaration that would be shadowed by a global lexical declaration.
|
||||
return {};
|
||||
}));
|
||||
}
|
||||
|
||||
// b. Let thisEnv be lexEnv.
|
||||
auto* this_environment = lexical_environment;
|
||||
// c. Assert: The following loop will terminate.
|
||||
|
||||
// d. Repeat, while thisEnv is not the same as varEnv,
|
||||
while (this_environment != variable_environment) {
|
||||
// i. If thisEnv is not an object Environment Record, then
|
||||
if (!is<ObjectEnvironment>(*this_environment)) {
|
||||
program.for_each_var_declared_name([&](auto const& name) {
|
||||
// 1. NOTE: The environment of with statements cannot contain any lexical declaration so it doesn't need to be checked for var/let hoisting conflicts.
|
||||
// 2. For each element name of varNames, do
|
||||
TRY(program.for_each_var_declared_name([&](auto const& name) -> ThrowCompletionOr<void> {
|
||||
// a. If thisEnv.HasBinding(name) is true, then
|
||||
if (MUST(this_environment->has_binding(name))) {
|
||||
vm.throw_exception<SyntaxError>(global_object, ErrorType::TopLevelVariableAlreadyDeclared, name);
|
||||
return IterationDecision::Break;
|
||||
// i. Throw a SyntaxError exception.
|
||||
return vm.throw_completion<SyntaxError>(global_object, ErrorType::TopLevelVariableAlreadyDeclared, name);
|
||||
|
||||
// FIXME: ii. NOTE: Annex B.3.4 defines alternate semantics for the above step.
|
||||
// In particular it only throw the syntax error if it is not an environment from a catchclause.
|
||||
}
|
||||
// FIXME: NOTE: Annex B.3.4 defines alternate semantics for the above step.
|
||||
// In particular it only throw the syntax error if it is not an environment from a catchclause.
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
if (auto* exception = vm.exception())
|
||||
return throw_completion(exception->value());
|
||||
// b. NOTE: A direct eval will not hoist var declaration over a like-named lexical declaration.
|
||||
return {};
|
||||
}));
|
||||
}
|
||||
|
||||
// ii. Set thisEnv to thisEnv.[[OuterEnv]].
|
||||
this_environment = this_environment->outer_environment();
|
||||
VERIFY(this_environment);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Let privateIdentifiers be a new empty List.
|
||||
// 5. Let pointer be privateEnv.
|
||||
// 6. Repeat, while pointer is not null,
|
||||
// a. For each Private Name binding of pointer.[[Names]], do
|
||||
// i. If privateIdentifiers does not contain binding.[[Description]], append binding.[[Description]] to privateIdentifiers.
|
||||
// b. Set pointer to pointer.[[OuterPrivateEnvironment]].
|
||||
// 7. If AllPrivateIdentifiersValid of body with argument privateIdentifiers is false, throw a SyntaxError exception.
|
||||
// FIXME: Add Private identifiers check here.
|
||||
|
||||
HashTable<FlyString> declared_function_names;
|
||||
// 8. Let functionsToInitialize be a new empty List.
|
||||
Vector<FunctionDeclaration const&> functions_to_initialize;
|
||||
program.for_each_var_function_declaration_in_reverse_order([&](FunctionDeclaration const& function) {
|
||||
|
||||
// 9. Let declaredFunctionNames be a new empty List.
|
||||
HashTable<FlyString> declared_function_names;
|
||||
|
||||
// 10. For each element d of varDeclarations, in reverse List order, do
|
||||
TRY(program.for_each_var_function_declaration_in_reverse_order([&](FunctionDeclaration const& function) -> ThrowCompletionOr<void> {
|
||||
// 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.
|
||||
// Note: This is done by for_each_var_function_declaration_in_reverse_order.
|
||||
|
||||
// ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used.
|
||||
// iii. Let fn be the sole element of the BoundNames of d.
|
||||
// iv. If fn is not an element of declaredFunctionNames, then
|
||||
if (declared_function_names.set(function.name()) != AK::HashSetResult::InsertedNewEntry)
|
||||
return IterationDecision::Continue;
|
||||
return {};
|
||||
|
||||
// 1. If varEnv is a global Environment Record, then
|
||||
if (global_var_environment) {
|
||||
auto function_definable_or_error = global_var_environment->can_declare_global_function(function.name());
|
||||
if (function_definable_or_error.is_error())
|
||||
return IterationDecision::Break;
|
||||
auto function_definable = function_definable_or_error.release_value();
|
||||
// a. Let fnDefinable be ? varEnv.CanDeclareGlobalFunction(fn).
|
||||
|
||||
if (!function_definable) {
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::CannotDeclareGlobalFunction, function.name());
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
auto function_definable = TRY(global_var_environment->can_declare_global_function(function.name()));
|
||||
|
||||
// b. If fnDefinable is false, throw a TypeError exception.
|
||||
if (!function_definable)
|
||||
return vm.throw_completion<TypeError>(global_object, ErrorType::CannotDeclareGlobalFunction, function.name());
|
||||
}
|
||||
|
||||
// 2. Append fn to declaredFunctionNames.
|
||||
// Note: Already done in step iv.
|
||||
|
||||
// 3. Insert d as the first element of functionsToInitialize.
|
||||
functions_to_initialize.append(function);
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
if (auto* exception = vm.exception())
|
||||
return throw_completion(exception->value());
|
||||
return {};
|
||||
}));
|
||||
|
||||
// 11. NOTE: Annex B.3.2.3 adds additional steps at this point.
|
||||
// B.3.2.3 Changes to EvalDeclarationInstantiation, https://tc39.es/ecma262/#sec-web-compat-evaldeclarationinstantiation
|
||||
// 11. If strict is false, then
|
||||
if (!strict) {
|
||||
// a. Let declaredFunctionOrVarNames be the list-concatenation of declaredFunctionNames and declaredVarNames.
|
||||
// The spec here uses 'declaredVarNames' but that has not been declared yet.
|
||||
HashTable<FlyString> hoisted_functions;
|
||||
program.for_each_function_hoistable_with_annexB_extension([&](FunctionDeclaration& function_declaration) {
|
||||
|
||||
// b. For each FunctionDeclaration f that is directly contained in the StatementList of a Block, CaseClause, or DefaultClause Contained within body, do
|
||||
TRY(program.for_each_function_hoistable_with_annexB_extension([&](FunctionDeclaration& function_declaration) -> ThrowCompletionOr<void> {
|
||||
// i. Let F be StringValue of the BindingIdentifier of f.
|
||||
auto& function_name = function_declaration.name();
|
||||
|
||||
// ii. If replacing the FunctionDeclaration f with a VariableStatement that has F as a BindingIdentifier would not produce any Early Errors for body, then
|
||||
// Note: This is checked during parsing and for_each_function_hoistable_with_annexB_extension so it always passes here.
|
||||
|
||||
// 1. Let bindingExists be false.
|
||||
// 2. Let thisEnv be lexEnv.
|
||||
auto* this_environment = lexical_environment;
|
||||
|
||||
while (this_environment != variable_environment) {
|
||||
if (!is<ObjectEnvironment>(*this_environment) && MUST(this_environment->has_binding(function_name)))
|
||||
return IterationDecision::Continue;
|
||||
// 3. Assert: The following loop will terminate.
|
||||
|
||||
// 4. Repeat, while thisEnv is not the same as varEnv,
|
||||
while (this_environment != variable_environment) {
|
||||
// a. If thisEnv is not an object Environment Record, then
|
||||
if (!is<ObjectEnvironment>(*this_environment)) {
|
||||
// i. If thisEnv.HasBinding(F) is true, then
|
||||
|
||||
if (MUST(this_environment->has_binding(function_name))) {
|
||||
// i. Let bindingExists be true.
|
||||
// Note: When bindingExists is true we skip all the other steps.
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// b. Set thisEnv to thisEnv.[[OuterEnv]].
|
||||
this_environment = this_environment->outer_environment();
|
||||
VERIFY(this_environment);
|
||||
}
|
||||
|
||||
// Note: At this point bindingExists is false.
|
||||
// 5. If bindingExists is false and varEnv is a global Environment Record, then
|
||||
if (global_var_environment) {
|
||||
if (global_var_environment->has_lexical_declaration(function_name))
|
||||
return IterationDecision::Continue;
|
||||
|
||||
auto var_definable_or_error = global_var_environment->can_declare_global_var(function_name);
|
||||
if (var_definable_or_error.is_error())
|
||||
return IterationDecision::Break;
|
||||
auto var_definable = var_definable_or_error.release_value();
|
||||
|
||||
if (!var_definable)
|
||||
return IterationDecision::Continue;
|
||||
// a. If varEnv.HasLexicalDeclaration(F) is false, then
|
||||
if (!global_var_environment->has_lexical_declaration(function_name)) {
|
||||
// i. Let fnDefinable be ? varEnv.CanDeclareGlobalVar(F).
|
||||
if (!TRY(global_var_environment->can_declare_global_var(function_name)))
|
||||
return {};
|
||||
}
|
||||
// b. Else,
|
||||
else {
|
||||
// i. Let fnDefinable be false.
|
||||
return {};
|
||||
}
|
||||
}
|
||||
// 6. Else,
|
||||
// a. Let fnDefinable be true.
|
||||
|
||||
// Note: At this point fnDefinable is true.
|
||||
// 7. If bindingExists is false and fnDefinable is true, then
|
||||
|
||||
// a. If declaredFunctionOrVarNames does not contain F, then
|
||||
if (!declared_function_names.contains(function_name) && !hoisted_functions.contains(function_name)) {
|
||||
// i. If varEnv is a global Environment Record, then
|
||||
if (global_var_environment) {
|
||||
auto result = global_var_environment->create_global_var_binding(function_name, true);
|
||||
if (result.is_error())
|
||||
return IterationDecision::Break;
|
||||
} else {
|
||||
// i. Perform ? varEnv.CreateGlobalVarBinding(F, true).
|
||||
TRY(global_var_environment->create_global_var_binding(function_name, true));
|
||||
}
|
||||
// ii. Else,
|
||||
else {
|
||||
|
||||
// i. Let bindingExists be varEnv.HasBinding(F).
|
||||
// ii. If bindingExists is false, then
|
||||
if (!MUST(variable_environment->has_binding(function_name))) {
|
||||
// i. Perform ! varEnv.CreateMutableBinding(F, true).
|
||||
// ii. Perform ! varEnv.InitializeBinding(F, undefined).
|
||||
MUST(variable_environment->create_mutable_binding(global_object, function_name, true));
|
||||
MUST(variable_environment->initialize_binding(global_object, function_name, js_undefined()));
|
||||
}
|
||||
}
|
||||
|
||||
hoisted_functions.set(function_name);
|
||||
}
|
||||
|
||||
// iii. Append F to declaredFunctionOrVarNames.
|
||||
hoisted_functions.set(function_name);
|
||||
|
||||
// b. When the FunctionDeclaration f is evaluated, perform the following steps in place of the FunctionDeclaration Evaluation algorithm provided in 15.2.6:
|
||||
// i. Let genv be the running execution context's VariableEnvironment.
|
||||
// ii. Let benv be the running execution context's LexicalEnvironment.
|
||||
// iii. Let fobj be ! benv.GetBindingValue(F, false).
|
||||
// iv. Perform ? genv.SetMutableBinding(F, fobj, false).
|
||||
// v. Return NormalCompletion(empty).
|
||||
function_declaration.set_should_do_additional_annexB_steps();
|
||||
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
if (auto* exception = vm.exception())
|
||||
return throw_completion(exception->value());
|
||||
return {};
|
||||
}));
|
||||
}
|
||||
|
||||
// 12. Let declaredVarNames be a new empty List.
|
||||
HashTable<FlyString> declared_var_names;
|
||||
|
||||
program.for_each_var_scoped_variable_declaration([&](VariableDeclaration const& declaration) {
|
||||
declaration.for_each_bound_name([&](auto const& name) {
|
||||
// 13. For each element d of varDeclarations, do
|
||||
TRY(program.for_each_var_scoped_variable_declaration([&](VariableDeclaration const& declaration) {
|
||||
// a. If d is a VariableDeclaration, a ForBinding, or a BindingIdentifier, then
|
||||
// Note: This is handled by for_each_var_scoped_variable_declaration.
|
||||
|
||||
// i. For each String vn of the BoundNames of d, do
|
||||
return declaration.for_each_bound_name([&](auto const& name) -> ThrowCompletionOr<void> {
|
||||
// 1. If vn is not an element of declaredFunctionNames, then
|
||||
if (!declared_function_names.contains(name)) {
|
||||
// a. If varEnv is a global Environment Record, then
|
||||
if (global_var_environment) {
|
||||
auto variable_definable_or_error = global_var_environment->can_declare_global_var(name);
|
||||
if (variable_definable_or_error.is_error())
|
||||
return IterationDecision::Break;
|
||||
auto variable_definable = variable_definable_or_error.release_value();
|
||||
if (!variable_definable) {
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::CannotDeclareGlobalVariable, name);
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
// i. Let vnDefinable be ? varEnv.CanDeclareGlobalVar(vn).
|
||||
auto variable_definable = TRY(global_var_environment->can_declare_global_var(name));
|
||||
|
||||
// ii. If vnDefinable is false, throw a TypeError exception.
|
||||
if (!variable_definable)
|
||||
return vm.throw_completion<TypeError>(global_object, ErrorType::CannotDeclareGlobalVariable, name);
|
||||
}
|
||||
|
||||
// b. If vn is not an element of declaredVarNames, then
|
||||
// i. Append vn to declaredVarNames.
|
||||
declared_var_names.set(name);
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
return {};
|
||||
});
|
||||
if (vm.exception())
|
||||
return IterationDecision::Break;
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
if (auto* exception = vm.exception())
|
||||
return throw_completion(exception->value());
|
||||
}));
|
||||
|
||||
// 14. NOTE: No abnormal terminations occur after this algorithm step unless varEnv is a global Environment Record and the global object is a Proxy exotic object.
|
||||
|
||||
program.for_each_lexically_scoped_declaration([&](Declaration const& declaration) {
|
||||
declaration.for_each_bound_name([&](auto const& name) {
|
||||
if (declaration.is_constant_declaration())
|
||||
(void)lexical_environment->create_immutable_binding(global_object, name, true);
|
||||
else
|
||||
(void)lexical_environment->create_mutable_binding(global_object, name, false);
|
||||
if (vm.exception())
|
||||
return IterationDecision::Break;
|
||||
return IterationDecision::Continue;
|
||||
// 15. Let lexDeclarations be the LexicallyScopedDeclarations of body.
|
||||
// 16. For each element d of lexDeclarations, do
|
||||
TRY(program.for_each_lexically_scoped_declaration([&](Declaration const& declaration) {
|
||||
// a. NOTE: Lexically declared names are only instantiated here but not initialized.
|
||||
|
||||
// b. For each element dn of the BoundNames of d, do
|
||||
return declaration.for_each_bound_name([&](auto const& name) -> ThrowCompletionOr<void> {
|
||||
// i. If IsConstantDeclaration of d is true, then
|
||||
if (declaration.is_constant_declaration()) {
|
||||
// 1. Perform ? lexEnv.CreateImmutableBinding(dn, true).
|
||||
TRY(lexical_environment->create_immutable_binding(global_object, name, true));
|
||||
}
|
||||
// ii. Else,
|
||||
else {
|
||||
// 1. Perform ? lexEnv.CreateMutableBinding(dn, false).
|
||||
TRY(lexical_environment->create_mutable_binding(global_object, name, false));
|
||||
}
|
||||
return {};
|
||||
});
|
||||
if (vm.exception())
|
||||
return IterationDecision::Break;
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
if (auto* exception = vm.exception())
|
||||
return throw_completion(exception->value());
|
||||
}));
|
||||
|
||||
// 17. For each Parse Node f of functionsToInitialize, do
|
||||
for (auto& declaration : 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(global_object, declaration.name(), declaration.source_text(), declaration.body(), declaration.parameters(), declaration.function_length(), lexical_environment, private_environment, declaration.kind(), declaration.is_strict_mode(), declaration.might_need_arguments_object());
|
||||
|
||||
// c. If varEnv is a global Environment Record, then
|
||||
if (global_var_environment) {
|
||||
// i. Perform ? varEnv.CreateGlobalFunctionBinding(fn, fo, true).
|
||||
TRY(global_var_environment->create_global_function_binding(declaration.name(), function, true));
|
||||
} else {
|
||||
}
|
||||
// d. Else,
|
||||
else {
|
||||
// i. Let bindingExists be varEnv.HasBinding(fn).
|
||||
auto binding_exists = MUST(variable_environment->has_binding(declaration.name()));
|
||||
|
||||
// ii. If bindingExists is false, then
|
||||
if (!binding_exists) {
|
||||
TRY(variable_environment->create_mutable_binding(global_object, declaration.name(), true));
|
||||
TRY(variable_environment->initialize_binding(global_object, declaration.name(), function));
|
||||
} else {
|
||||
TRY(variable_environment->set_mutable_binding(global_object, declaration.name(), function, false));
|
||||
// 1. Let status be ! varEnv.CreateMutableBinding(fn, true).
|
||||
// 2. Assert: status is not an abrupt completion because of validation preceding step 14.
|
||||
MUST(variable_environment->create_mutable_binding(global_object, declaration.name(), true));
|
||||
|
||||
// 3. Perform ! varEnv.InitializeBinding(fn, fo).
|
||||
MUST(variable_environment->initialize_binding(global_object, declaration.name(), function));
|
||||
}
|
||||
// iii. Else,
|
||||
else {
|
||||
// 1. Perform ! varEnv.SetMutableBinding(fn, fo, false).
|
||||
MUST(variable_environment->set_mutable_binding(global_object, declaration.name(), function, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 18. For each String vn of declaredVarNames, do
|
||||
|
||||
for (auto& var_name : declared_var_names) {
|
||||
// a. If varEnv is a global Environment Record, then
|
||||
if (global_var_environment) {
|
||||
// i. Perform ? varEnv.CreateGlobalVarBinding(vn, true).
|
||||
TRY(global_var_environment->create_global_var_binding(var_name, true));
|
||||
} else {
|
||||
}
|
||||
// b. Else,
|
||||
else {
|
||||
// i. Let bindingExists be varEnv.HasBinding(vn).
|
||||
auto binding_exists = MUST(variable_environment->has_binding(var_name));
|
||||
|
||||
// ii. If bindingExists is false, then
|
||||
if (!binding_exists) {
|
||||
TRY(variable_environment->create_mutable_binding(global_object, var_name, true));
|
||||
TRY(variable_environment->initialize_binding(global_object, var_name, js_undefined()));
|
||||
// 1. Let status be ! varEnv.CreateMutableBinding(vn, true).
|
||||
// 2. Assert: status is not an abrupt completion because of validation preceding step 14.
|
||||
MUST(variable_environment->create_mutable_binding(global_object, var_name, true));
|
||||
|
||||
// 3. Perform ! varEnv.InitializeBinding(vn, undefined).
|
||||
MUST(variable_environment->initialize_binding(global_object, var_name, js_undefined()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 19. Return NormalCompletion(empty).
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -375,11 +375,8 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
|
||||
if (!has_parameter_expressions && arguments_object_needed) {
|
||||
scope_body->for_each_lexically_declared_name([&](auto const& name) {
|
||||
if (name == arguments_name) {
|
||||
if (name == arguments_name)
|
||||
arguments_object_needed = false;
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
@ -485,7 +482,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
if (scope_body) {
|
||||
scope_body->for_each_var_declared_name([&](auto const& name) {
|
||||
if (instantiated_var_names.set(name) != AK::HashSetResult::InsertedNewEntry)
|
||||
return IterationDecision::Continue;
|
||||
return;
|
||||
MUST(var_environment->create_mutable_binding(global_object(), name, false));
|
||||
|
||||
Value initial_value;
|
||||
|
@ -495,8 +492,6 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
initial_value = MUST(environment->get_binding_value(global_object(), name, false));
|
||||
|
||||
MUST(var_environment->initialize_binding(global_object(), name, initial_value));
|
||||
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -506,7 +501,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
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 IterationDecision::Continue;
|
||||
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(global_object(), function_name, false));
|
||||
|
@ -515,7 +510,6 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
}
|
||||
|
||||
function_declaration.set_should_do_additional_annexB_steps();
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -554,7 +548,6 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
MUST(lex_environment->create_immutable_binding(global_object(), name, true));
|
||||
else
|
||||
MUST(lex_environment->create_mutable_binding(global_object(), name, false));
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue