1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 11:18:11 +00:00

LibJS: Don't discard ThrowCompletionOr<void> from declaration iteration

This commit is contained in:
Luke Wilde 2023-02-27 22:13:37 +00:00 committed by Linus Groh
parent a964ebc255
commit f4be95af69
5 changed files with 130 additions and 81 deletions

View file

@ -779,14 +779,16 @@ Completion ForStatement::loop_evaluation(Interpreter& interpreter, Vector<Deprec
if (declaration) { if (declaration) {
loop_env = new_declarative_environment(*old_environment); loop_env = new_declarative_environment(*old_environment);
auto is_const = declaration->is_constant_declaration(); auto is_const = declaration->is_constant_declaration();
declaration->for_each_bound_name([&](auto const& name) { // NOTE: Due to the use of MUST with `create_immutable_binding` and `create_mutable_binding` below,
// an exception should not result from `for_each_bound_name`.
MUST(declaration->for_each_bound_name([&](auto const& name) {
if (is_const) { if (is_const) {
MUST(loop_env->create_immutable_binding(vm, name, true)); MUST(loop_env->create_immutable_binding(vm, name, true));
} else { } else {
MUST(loop_env->create_mutable_binding(vm, name, false)); MUST(loop_env->create_mutable_binding(vm, name, false));
++per_iteration_bindings_size; ++per_iteration_bindings_size;
} }
}); }));
interpreter.vm().running_execution_context().lexical_environment = loop_env; interpreter.vm().running_execution_context().lexical_environment = loop_env;
} }
@ -979,7 +981,9 @@ struct ForInOfHeadState {
// 14.7.5.4 Runtime Semantics: ForDeclarationBindingInstantiation, https://tc39.es/ecma262/#sec-runtime-semantics-fordeclarationbindinginstantiation // 14.7.5.4 Runtime Semantics: ForDeclarationBindingInstantiation, https://tc39.es/ecma262/#sec-runtime-semantics-fordeclarationbindinginstantiation
// 1. For each element name of the BoundNames of ForBinding, do // 1. For each element name of the BoundNames of ForBinding, do
for_declaration.for_each_bound_name([&](auto const& name) { // NOTE: Due to the use of MUST with `create_immutable_binding` and `create_mutable_binding` below,
// an exception should not result from `for_each_bound_name`.
MUST(for_declaration.for_each_bound_name([&](auto const& name) {
if (first_name.is_empty()) if (first_name.is_empty())
first_name = name; first_name = name;
@ -993,7 +997,7 @@ struct ForInOfHeadState {
// i. Perform ! environment.CreateMutableBinding(name, false). // i. Perform ! environment.CreateMutableBinding(name, false).
MUST(iteration_environment->create_mutable_binding(vm, name, false)); MUST(iteration_environment->create_mutable_binding(vm, name, false));
} }
}); }));
interpreter.vm().running_execution_context().lexical_environment = iteration_environment; interpreter.vm().running_execution_context().lexical_environment = iteration_environment;
if (!destructuring) { if (!destructuring) {
@ -1074,18 +1078,20 @@ static ThrowCompletionOr<ForInOfHeadState> for_in_of_head_execute(Interpreter& i
} else { } else {
state.lhs_kind = ForInOfHeadState::LexicalBinding; state.lhs_kind = ForInOfHeadState::LexicalBinding;
new_environment = new_declarative_environment(*interpreter.lexical_environment()); new_environment = new_declarative_environment(*interpreter.lexical_environment());
variable_declaration.for_each_bound_name([&](auto const& name) { // NOTE: Due to the use of MUST with `create_mutable_binding` below, an exception should not result from `for_each_bound_name`.
MUST(variable_declaration.for_each_bound_name([&](auto const& name) {
MUST(new_environment->create_mutable_binding(vm, name, false)); MUST(new_environment->create_mutable_binding(vm, name, false));
}); }));
} }
} else { } else {
VERIFY(is<UsingDeclaration>(ast_ptr->ptr())); VERIFY(is<UsingDeclaration>(ast_ptr->ptr()));
auto& declaration = static_cast<UsingDeclaration const&>(*(*ast_ptr)); auto& declaration = static_cast<UsingDeclaration const&>(*(*ast_ptr));
state.lhs_kind = ForInOfHeadState::LexicalBinding; state.lhs_kind = ForInOfHeadState::LexicalBinding;
new_environment = new_declarative_environment(*interpreter.lexical_environment()); new_environment = new_declarative_environment(*interpreter.lexical_environment());
declaration.for_each_bound_name([&](auto const& name) { // NOTE: Due to the use of MUST with `create_mutable_binding` below, an exception should not result from `for_each_bound_name`.
MUST(declaration.for_each_bound_name([&](auto const& name) {
MUST(new_environment->create_mutable_binding(vm, name, false)); MUST(new_environment->create_mutable_binding(vm, name, false));
}); }));
} }
if (new_environment) { if (new_environment) {
@ -2071,7 +2077,7 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::class_definition_e
class_constructor->add_private_method(private_method); class_constructor->add_private_method(private_method);
for (auto& method : static_private_methods) for (auto& method : static_private_methods)
class_constructor->private_method_or_accessor_add(move(method)); TRY(class_constructor->private_method_or_accessor_add(move(method)));
for (auto& element : static_elements) { for (auto& element : static_elements) {
TRY(element.visit( TRY(element.visit(
@ -3983,10 +3989,11 @@ Completion TryStatement::execute(Interpreter& interpreter) const
}, },
[&](NonnullRefPtr<BindingPattern const> const& pattern) { [&](NonnullRefPtr<BindingPattern const> const& pattern) {
// 3. For each element argName of the BoundNames of CatchParameter, do // 3. For each element argName of the BoundNames of CatchParameter, do
pattern->for_each_bound_name([&](auto& name) { // NOTE: Due to the use of MUST with `create_mutable_binding` below, an exception should not result from `for_each_bound_name`.
MUST(pattern->for_each_bound_name([&](auto& name) {
// a. Perform ! catchEnv.CreateMutableBinding(argName, false). // a. Perform ! catchEnv.CreateMutableBinding(argName, false).
MUST(catch_environment->create_mutable_binding(vm, name, false)); MUST(catch_environment->create_mutable_binding(vm, name, false));
}); }));
}); });
// 4. Set the running execution context's LexicalEnvironment to catchEnv. // 4. Set the running execution context's LexicalEnvironment to catchEnv.
@ -4725,16 +4732,19 @@ void ScopeNode::block_declaration_instantiation(Interpreter& interpreter, Enviro
VERIFY(environment); VERIFY(environment);
auto* private_environment = vm.running_execution_context().private_environment; auto* private_environment = vm.running_execution_context().private_environment;
// Note: All the calls here are ! and thus we do not need to TRY this callback. // Note: All the calls here are ! and thus we do not need to TRY this callback.
for_each_lexically_scoped_declaration([&](Declaration const& declaration) { // We use MUST to ensure it does not throw and to avoid discarding the returned ThrowCompletionOr<void>.
MUST(for_each_lexically_scoped_declaration([&](Declaration const& declaration) {
auto is_constant_declaration = declaration.is_constant_declaration(); auto is_constant_declaration = declaration.is_constant_declaration();
declaration.for_each_bound_name([&](auto const& name) { // NOTE: Due to the use of MUST with `create_immutable_binding` and `create_mutable_binding` below,
// an exception should not result from `for_each_bound_name`.
MUST(declaration.for_each_bound_name([&](auto const& name) {
if (is_constant_declaration) { if (is_constant_declaration) {
MUST(environment->create_immutable_binding(vm, name, true)); MUST(environment->create_immutable_binding(vm, name, true));
} else { } else {
if (!MUST(environment->has_binding(name))) if (!MUST(environment->has_binding(name)))
MUST(environment->create_mutable_binding(vm, name, false)); MUST(environment->create_mutable_binding(vm, name, false));
} }
}); }));
if (is<FunctionDeclaration>(declaration)) { if (is<FunctionDeclaration>(declaration)) {
auto& function_declaration = static_cast<FunctionDeclaration const&>(declaration); auto& function_declaration = static_cast<FunctionDeclaration const&>(declaration);
@ -4742,7 +4752,7 @@ void ScopeNode::block_declaration_instantiation(Interpreter& interpreter, Enviro
VERIFY(is<DeclarativeEnvironment>(*environment)); VERIFY(is<DeclarativeEnvironment>(*environment));
static_cast<DeclarativeEnvironment&>(*environment).initialize_or_set_mutable_binding({}, vm, function_declaration.name(), function); static_cast<DeclarativeEnvironment&>(*environment).initialize_or_set_mutable_binding({}, vm, function_declaration.name(), function);
} }
}); }));
} }
// 16.1.7 GlobalDeclarationInstantiation ( script, env ), https://tc39.es/ecma262/#sec-globaldeclarationinstantiation // 16.1.7 GlobalDeclarationInstantiation ( script, env ), https://tc39.es/ecma262/#sec-globaldeclarationinstantiation

View file

@ -44,7 +44,8 @@ Bytecode::CodeGenerationErrorOr<void> ScopeNode::generate_bytecode(Bytecode::Gen
(void)for_each_lexically_scoped_declaration([&](Declaration const& declaration) -> ThrowCompletionOr<void> { (void)for_each_lexically_scoped_declaration([&](Declaration const& declaration) -> ThrowCompletionOr<void> {
auto is_constant_declaration = declaration.is_constant_declaration(); auto is_constant_declaration = declaration.is_constant_declaration();
declaration.for_each_bound_name([&](auto const& name) { // NOTE: Nothing in the callback throws an exception.
MUST(declaration.for_each_bound_name([&](auto const& name) {
auto index = generator.intern_identifier(name); auto index = generator.intern_identifier(name);
// NOTE: BlockDeclarationInstantiation takes as input the new lexical environment that was created and checks if there is a binding for the current name only in this new scope. // NOTE: BlockDeclarationInstantiation takes as input the new lexical environment that was created and checks if there is a binding for the current name only in this new scope.
// For example: `{ let a = 1; { let a = 2; } }`. The second `a` will shadow the first `a` instead of re-initializing or setting it. // For example: `{ let a = 1; { let a = 2; } }`. The second `a` will shadow the first `a` instead of re-initializing or setting it.
@ -52,7 +53,7 @@ Bytecode::CodeGenerationErrorOr<void> ScopeNode::generate_bytecode(Bytecode::Gen
generator.register_binding(index); generator.register_binding(index);
generator.emit<Bytecode::Op::CreateVariable>(index, Bytecode::Op::EnvironmentMode::Lexical, is_constant_declaration); generator.emit<Bytecode::Op::CreateVariable>(index, Bytecode::Op::EnvironmentMode::Lexical, is_constant_declaration);
} }
}); }));
if (is<FunctionDeclaration>(declaration)) { if (is<FunctionDeclaration>(declaration)) {
auto& function_declaration = static_cast<FunctionDeclaration const&>(declaration); auto& function_declaration = static_cast<FunctionDeclaration const&>(declaration);
@ -257,13 +258,14 @@ Bytecode::CodeGenerationErrorOr<void> ScopeNode::generate_bytecode(Bytecode::Gen
// FIXME: Implement this boi correctly. // FIXME: Implement this boi correctly.
(void)for_each_lexically_scoped_declaration([&](Declaration const& declaration) -> ThrowCompletionOr<void> { (void)for_each_lexically_scoped_declaration([&](Declaration const& declaration) -> ThrowCompletionOr<void> {
auto is_constant_declaration = declaration.is_constant_declaration(); auto is_constant_declaration = declaration.is_constant_declaration();
declaration.for_each_bound_name([&](auto const& name) { // NOTE: Nothing in the callback throws an exception.
MUST(declaration.for_each_bound_name([&](auto const& name) {
auto index = generator.intern_identifier(name); auto index = generator.intern_identifier(name);
if (is_constant_declaration || !generator.has_binding(index)) { if (is_constant_declaration || !generator.has_binding(index)) {
generator.register_binding(index); generator.register_binding(index);
generator.emit<Bytecode::Op::CreateVariable>(index, Bytecode::Op::EnvironmentMode::Lexical, is_constant_declaration); generator.emit<Bytecode::Op::CreateVariable>(index, Bytecode::Op::EnvironmentMode::Lexical, is_constant_declaration);
} }
}); }));
if (is<FunctionDeclaration>(declaration)) { if (is<FunctionDeclaration>(declaration)) {
auto& function_declaration = static_cast<FunctionDeclaration const&>(declaration); auto& function_declaration = static_cast<FunctionDeclaration const&>(declaration);
@ -1001,11 +1003,12 @@ Bytecode::CodeGenerationErrorOr<void> ForStatement::generate_labelled_evaluation
generator.begin_variable_scope(Bytecode::Generator::BindingMode::Lexical, Bytecode::Generator::SurroundingScopeKind::Block); generator.begin_variable_scope(Bytecode::Generator::BindingMode::Lexical, Bytecode::Generator::SurroundingScopeKind::Block);
bool is_const = variable_declaration.is_constant_declaration(); bool is_const = variable_declaration.is_constant_declaration();
variable_declaration.for_each_bound_name([&](auto const& name) { // NOTE: Nothing in the callback throws an exception.
MUST(variable_declaration.for_each_bound_name([&](auto const& name) {
auto index = generator.intern_identifier(name); auto index = generator.intern_identifier(name);
generator.register_binding(index); generator.register_binding(index);
generator.emit<Bytecode::Op::CreateVariable>(index, Bytecode::Op::EnvironmentMode::Lexical, is_const); generator.emit<Bytecode::Op::CreateVariable>(index, Bytecode::Op::EnvironmentMode::Lexical, is_const);
}); }));
} }
} }
@ -2475,12 +2478,13 @@ static Bytecode::CodeGenerationErrorOr<ForInOfHeadEvaluationResult> for_in_of_he
// b. Let newEnv be NewDeclarativeEnvironment(oldEnv). // b. Let newEnv be NewDeclarativeEnvironment(oldEnv).
generator.begin_variable_scope(); generator.begin_variable_scope();
// c. For each String name of uninitializedBoundNames, do // c. For each String name of uninitializedBoundNames, do
variable_declaration.for_each_bound_name([&](auto const& name) { // NOTE: Nothing in the callback throws an exception.
MUST(variable_declaration.for_each_bound_name([&](auto const& name) {
// i. Perform ! newEnv.CreateMutableBinding(name, false). // i. Perform ! newEnv.CreateMutableBinding(name, false).
auto identifier = generator.intern_identifier(name); auto identifier = generator.intern_identifier(name);
generator.register_binding(identifier); generator.register_binding(identifier);
generator.emit<Bytecode::Op::CreateVariable>(identifier, Bytecode::Op::EnvironmentMode::Lexical, false); generator.emit<Bytecode::Op::CreateVariable>(identifier, Bytecode::Op::EnvironmentMode::Lexical, false);
}); }));
// d. Set the running execution context's LexicalEnvironment to newEnv. // d. Set the running execution context's LexicalEnvironment to newEnv.
// NOTE: Done by CreateEnvironment. // NOTE: Done by CreateEnvironment.
} }
@ -2634,7 +2638,8 @@ static Bytecode::CodeGenerationErrorOr<void> for_in_of_body_evaluation(Bytecode:
// NOTE: We just made it. // NOTE: We just made it.
auto& variable_declaration = static_cast<VariableDeclaration const&>(*lhs.get<NonnullRefPtr<ASTNode const>>()); auto& variable_declaration = static_cast<VariableDeclaration const&>(*lhs.get<NonnullRefPtr<ASTNode const>>());
// 2. For each element name of the BoundNames of ForBinding, do // 2. For each element name of the BoundNames of ForBinding, do
variable_declaration.for_each_bound_name([&](auto const& name) { // NOTE: Nothing in the callback throws an exception.
MUST(variable_declaration.for_each_bound_name([&](auto const& name) {
auto identifier = generator.intern_identifier(name); auto identifier = generator.intern_identifier(name);
generator.register_binding(identifier, Bytecode::Generator::BindingMode::Lexical); generator.register_binding(identifier, Bytecode::Generator::BindingMode::Lexical);
// a. If IsConstantDeclaration of LetOrConst is true, then // a. If IsConstantDeclaration of LetOrConst is true, then
@ -2647,7 +2652,7 @@ static Bytecode::CodeGenerationErrorOr<void> for_in_of_body_evaluation(Bytecode:
// i. Perform ! environment.CreateMutableBinding(name, false). // i. Perform ! environment.CreateMutableBinding(name, false).
generator.emit<Bytecode::Op::CreateVariable>(identifier, Bytecode::Op::EnvironmentMode::Lexical, false); generator.emit<Bytecode::Op::CreateVariable>(identifier, Bytecode::Op::EnvironmentMode::Lexical, false);
} }
}); }));
// 3. Return unused. // 3. Return unused.
// NOTE: No need to do that as we've inlined this. // NOTE: No need to do that as we've inlined this.

View file

@ -70,9 +70,10 @@ public:
scope_pusher.m_forbidden_lexical_names.set(name); scope_pusher.m_forbidden_lexical_names.set(name);
}, },
[&](NonnullRefPtr<BindingPattern const> const& binding_pattern) { [&](NonnullRefPtr<BindingPattern const> const& binding_pattern) {
binding_pattern->for_each_bound_name([&](auto const& name) { // NOTE: Nothing in the callback throws an exception.
MUST(binding_pattern->for_each_bound_name([&](auto const& name) {
scope_pusher.m_forbidden_lexical_names.set(name); scope_pusher.m_forbidden_lexical_names.set(name);
}); }));
}); });
} }
return scope_pusher; return scope_pusher;
@ -94,9 +95,10 @@ public:
if (init && is<VariableDeclaration>(*init)) { if (init && is<VariableDeclaration>(*init)) {
auto& variable_declaration = static_cast<VariableDeclaration const&>(*init); auto& variable_declaration = static_cast<VariableDeclaration const&>(*init);
if (variable_declaration.declaration_kind() != DeclarationKind::Var) { if (variable_declaration.declaration_kind() != DeclarationKind::Var) {
variable_declaration.for_each_bound_name([&](auto const& name) { // NOTE: Nothing in the callback throws an exception.
MUST(variable_declaration.for_each_bound_name([&](auto const& name) {
scope_pusher.m_forbidden_var_names.set(name); scope_pusher.m_forbidden_var_names.set(name);
}); }));
} }
} }
@ -107,9 +109,10 @@ public:
{ {
ScopePusher scope_pusher(parser, nullptr, ScopeLevel::NotTopLevel); ScopePusher scope_pusher(parser, nullptr, ScopeLevel::NotTopLevel);
if (pattern) { if (pattern) {
pattern->for_each_bound_name([&](auto const& name) { // NOTE: Nothing in the callback throws an exception.
MUST(pattern->for_each_bound_name([&](auto const& name) {
scope_pusher.m_forbidden_var_names.set(name); scope_pusher.m_forbidden_var_names.set(name);
}); }));
} else if (!parameter.is_empty()) { } else if (!parameter.is_empty()) {
scope_pusher.m_var_names.set(parameter); scope_pusher.m_var_names.set(parameter);
} }
@ -129,17 +132,19 @@ public:
void add_declaration(NonnullRefPtr<Declaration const> declaration) void add_declaration(NonnullRefPtr<Declaration const> declaration)
{ {
if (declaration->is_lexical_declaration()) { if (declaration->is_lexical_declaration()) {
declaration->for_each_bound_name([&](auto const& name) { // NOTE: Nothing in the callback throws an exception.
MUST(declaration->for_each_bound_name([&](auto const& name) {
if (m_var_names.contains(name) || m_forbidden_lexical_names.contains(name) || m_function_names.contains(name)) if (m_var_names.contains(name) || m_forbidden_lexical_names.contains(name) || m_function_names.contains(name))
throw_identifier_declared(name, declaration); throw_identifier_declared(name, declaration);
if (m_lexical_names.set(name) != AK::HashSetResult::InsertedNewEntry) if (m_lexical_names.set(name) != AK::HashSetResult::InsertedNewEntry)
throw_identifier_declared(name, declaration); throw_identifier_declared(name, declaration);
}); }));
m_node->add_lexical_declaration(move(declaration)); m_node->add_lexical_declaration(move(declaration));
} else if (!declaration->is_function_declaration()) { } else if (!declaration->is_function_declaration()) {
declaration->for_each_bound_name([&](auto const& name) { // NOTE: Nothing in the callback throws an exception.
MUST(declaration->for_each_bound_name([&](auto const& name) {
ScopePusher* pusher = this; ScopePusher* pusher = this;
while (true) { while (true) {
if (pusher->m_lexical_names.contains(name) if (pusher->m_lexical_names.contains(name)
@ -156,16 +161,17 @@ public:
} }
VERIFY(pusher->is_top_level() && pusher->m_node); VERIFY(pusher->is_top_level() && pusher->m_node);
pusher->m_node->add_var_scoped_declaration(declaration); pusher->m_node->add_var_scoped_declaration(declaration);
}); }));
VERIFY(m_top_level_scope); VERIFY(m_top_level_scope);
m_top_level_scope->m_node->add_var_scoped_declaration(move(declaration)); m_top_level_scope->m_node->add_var_scoped_declaration(move(declaration));
} else { } else {
if (m_scope_level != ScopeLevel::NotTopLevel && m_scope_level != ScopeLevel::ModuleTopLevel) { if (m_scope_level != ScopeLevel::NotTopLevel && m_scope_level != ScopeLevel::ModuleTopLevel) {
// Only non-top levels and Module don't var declare the top functions // Only non-top levels and Module don't var declare the top functions
declaration->for_each_bound_name([&](auto const& name) { // NOTE: Nothing in the callback throws an exception.
MUST(declaration->for_each_bound_name([&](auto const& name) {
m_var_names.set(name); m_var_names.set(name);
}); }));
m_node->add_var_scoped_declaration(move(declaration)); m_node->add_var_scoped_declaration(move(declaration));
} else { } else {
VERIFY(is<FunctionDeclaration>(*declaration)); VERIFY(is<FunctionDeclaration>(*declaration));
@ -567,16 +573,18 @@ void Parser::parse_module(Program& program)
auto const& exported_name = entry.local_or_import_name; auto const& exported_name = entry.local_or_import_name;
bool found = false; bool found = false;
program.for_each_lexically_declared_name([&](auto const& name) { // NOTE: Nothing in the callback throws an exception.
MUST(program.for_each_lexically_declared_name([&](auto const& name) {
if (name == exported_name) if (name == exported_name)
found = true; found = true;
}); }));
if (found) if (found)
continue; continue;
program.for_each_var_declared_name([&](auto const& name) { // NOTE: Nothing in the callback throws an exception.
MUST(program.for_each_var_declared_name([&](auto const& name) {
if (name == exported_name) if (name == exported_name)
found = true; found = true;
}); }));
for (auto& import : program.imports()) { for (auto& import : program.imports()) {
if (import.has_bound_name(exported_name)) { if (import.has_bound_name(exported_name)) {
found = true; found = true;
@ -2532,7 +2540,8 @@ NonnullRefPtr<FunctionBody const> Parser::parse_function_body(Vector<FunctionPar
parameter_names.append(parameter_name); parameter_names.append(parameter_name);
}, },
[&](NonnullRefPtr<BindingPattern const> const& binding) { [&](NonnullRefPtr<BindingPattern const> const& binding) {
binding->for_each_bound_name([&](auto& bound_name) { // NOTE: Nothing in the callback throws an exception.
MUST(binding->for_each_bound_name([&](auto& bound_name) {
if (function_kind == FunctionKind::Generator && bound_name == "yield"sv) if (function_kind == FunctionKind::Generator && bound_name == "yield"sv)
syntax_error("Parameter name 'yield' not allowed in this context"); syntax_error("Parameter name 'yield' not allowed in this context");
@ -2546,7 +2555,7 @@ NonnullRefPtr<FunctionBody const> Parser::parse_function_body(Vector<FunctionPar
} }
} }
parameter_names.append(bound_name); parameter_names.append(bound_name);
}); }));
}); });
} }
} }
@ -2686,10 +2695,11 @@ Vector<FunctionParameter> Parser::parse_formal_parameters(int& function_length,
}, },
[&](NonnullRefPtr<BindingPattern const> const& bindings) { [&](NonnullRefPtr<BindingPattern const> const& bindings) {
bool found_duplicate = false; bool found_duplicate = false;
bindings->for_each_bound_name([&](auto& bound_name) { // NOTE: Nothing in the callback throws an exception.
MUST(bindings->for_each_bound_name([&](auto& bound_name) {
if (bound_name == parameter_name) if (bound_name == parameter_name)
found_duplicate = true; found_duplicate = true;
}); }));
return found_duplicate; return found_duplicate;
}); });
@ -2951,14 +2961,15 @@ RefPtr<BindingPattern const> Parser::parse_binding_pattern(Parser::AllowDuplicat
pattern->kind = kind; pattern->kind = kind;
Vector<StringView> bound_names; Vector<StringView> bound_names;
pattern->for_each_bound_name([&](auto& name) { // NOTE: Nothing in the callback throws an exception.
MUST(pattern->for_each_bound_name([&](auto& name) {
if (allow_duplicates == AllowDuplicates::No) { if (allow_duplicates == AllowDuplicates::No) {
if (bound_names.contains_slow(name)) if (bound_names.contains_slow(name))
syntax_error("Duplicate parameter names in bindings"); syntax_error("Duplicate parameter names in bindings");
bound_names.append(name); bound_names.append(name);
} }
check_identifier_name_for_assignment_validity(name); check_identifier_name_for_assignment_validity(name);
}); }));
return pattern; return pattern;
} }
@ -3018,10 +3029,11 @@ NonnullRefPtr<VariableDeclaration const> Parser::parse_variable_declaration(IsFo
Variant<NonnullRefPtr<Identifier const>, NonnullRefPtr<BindingPattern const>, Empty> target {}; Variant<NonnullRefPtr<Identifier const>, NonnullRefPtr<BindingPattern const>, Empty> target {};
if (auto pattern = parse_binding_pattern(declaration_kind != DeclarationKind::Var ? AllowDuplicates::No : AllowDuplicates::Yes, AllowMemberExpressions::No)) { if (auto pattern = parse_binding_pattern(declaration_kind != DeclarationKind::Var ? AllowDuplicates::No : AllowDuplicates::Yes, AllowMemberExpressions::No)) {
if ((declaration_kind == DeclarationKind::Let || declaration_kind == DeclarationKind::Const)) { if ((declaration_kind == DeclarationKind::Let || declaration_kind == DeclarationKind::Const)) {
pattern->for_each_bound_name([this](auto& name) { // NOTE: Nothing in the callback throws an exception.
MUST(pattern->for_each_bound_name([this](auto& name) {
if (name == "let"sv) if (name == "let"sv)
syntax_error("Lexical binding may not be called 'let'"); syntax_error("Lexical binding may not be called 'let'");
}); }));
} }
target = pattern.release_nonnull(); target = pattern.release_nonnull();
@ -3454,11 +3466,12 @@ NonnullRefPtr<CatchClause const> Parser::parse_catch_clause()
HashTable<DeprecatedFlyString> bound_names; HashTable<DeprecatedFlyString> bound_names;
if (pattern_parameter) { if (pattern_parameter) {
pattern_parameter->for_each_bound_name( // NOTE: Nothing in the callback throws an exception.
MUST(pattern_parameter->for_each_bound_name(
[&](auto& name) { [&](auto& name) {
check_identifier_name_for_assignment_validity(name); check_identifier_name_for_assignment_validity(name);
bound_names.set(name); bound_names.set(name);
}); }));
} }
if (!parameter.is_empty()) { if (!parameter.is_empty()) {
@ -3469,10 +3482,11 @@ NonnullRefPtr<CatchClause const> Parser::parse_catch_clause()
ScopePusher catch_scope = ScopePusher::catch_scope(*this, pattern_parameter, parameter); ScopePusher catch_scope = ScopePusher::catch_scope(*this, pattern_parameter, parameter);
auto body = parse_block_statement(); auto body = parse_block_statement();
body->for_each_lexically_declared_name([&](auto const& name) { // NOTE: Nothing in the callback throws an exception.
MUST(body->for_each_lexically_declared_name([&](auto const& name) {
if (bound_names.contains(name)) if (bound_names.contains(name))
syntax_error(DeprecatedString::formatted("Identifier '{}' already declared as catch parameter", name)); syntax_error(DeprecatedString::formatted("Identifier '{}' already declared as catch parameter", name));
}); }));
if (pattern_parameter) { if (pattern_parameter) {
return create_ast_node<CatchClause>( return create_ast_node<CatchClause>(
@ -3612,10 +3626,11 @@ NonnullRefPtr<Statement const> Parser::parse_for_statement()
} else { } else {
// This does not follow the normal declaration structure so we need additional checks. // This does not follow the normal declaration structure so we need additional checks.
HashTable<DeprecatedFlyString> bound_names; HashTable<DeprecatedFlyString> bound_names;
declaration->for_each_bound_name([&](auto const& name) { // NOTE: Nothing in the callback throws an exception.
MUST(declaration->for_each_bound_name([&](auto const& name) {
if (bound_names.set(name) != AK::HashSetResult::InsertedNewEntry) if (bound_names.set(name) != AK::HashSetResult::InsertedNewEntry)
syntax_error(DeprecatedString::formatted("Identifier '{}' already declared in for loop initializer", name), declaration->source_range().start); syntax_error(DeprecatedString::formatted("Identifier '{}' already declared in for loop initializer", name), declaration->source_range().start);
}); }));
} }
if (match_for_in_of()) { if (match_for_in_of()) {
@ -4650,9 +4665,10 @@ NonnullRefPtr<ExportStatement const> Parser::parse_export_statement(Program& pro
entries_with_location.append({ ExportEntry::named_export(identifier->string(), identifier->string()), identifier->source_range().start }); entries_with_location.append({ ExportEntry::named_export(identifier->string(), identifier->string()), identifier->source_range().start });
}, },
[&](NonnullRefPtr<BindingPattern const> const& binding) { [&](NonnullRefPtr<BindingPattern const> const& binding) {
binding->for_each_bound_name([&](auto& name) { // NOTE: Nothing in the callback throws an exception.
MUST(binding->for_each_bound_name([&](auto& name) {
entries_with_location.append({ ExportEntry::named_export(name, name), decl_position }); entries_with_location.append({ ExportEntry::named_export(name, name), decl_position });
}); }));
}); });
} }
} }
@ -4667,9 +4683,10 @@ NonnullRefPtr<ExportStatement const> Parser::parse_export_statement(Program& pro
entries_with_location.append({ ExportEntry::named_export(identifier->string(), identifier->string()), identifier->source_range().start }); entries_with_location.append({ ExportEntry::named_export(identifier->string(), identifier->string()), identifier->source_range().start });
}, },
[&](NonnullRefPtr<BindingPattern const> const& binding) { [&](NonnullRefPtr<BindingPattern const> const& binding) {
binding->for_each_bound_name([&](auto& name) { // NOTE: Nothing in the callback throws an exception.
MUST(binding->for_each_bound_name([&](auto& name) {
entries_with_location.append({ ExportEntry::named_export(name, name), variable_position }); entries_with_location.append({ ExportEntry::named_export(name, name), variable_position });
}); }));
}); });
} }
expression = variable_declaration; expression = variable_declaration;

View file

@ -355,10 +355,11 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
if (pattern->contains_expression()) if (pattern->contains_expression())
has_parameter_expressions = true; has_parameter_expressions = true;
pattern->for_each_bound_name([&](auto& name) { // NOTE: Nothing in the callback throws an exception.
MUST(pattern->for_each_bound_name([&](auto& name) {
if (parameter_names.set(name) != AK::HashSetResult::InsertedNewEntry) if (parameter_names.set(name) != AK::HashSetResult::InsertedNewEntry)
has_duplicates = true; has_duplicates = true;
}); }));
}); });
} }
@ -374,10 +375,11 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
Vector<FunctionDeclaration const&> functions_to_initialize; Vector<FunctionDeclaration const&> functions_to_initialize;
if (scope_body) { if (scope_body) {
scope_body->for_each_var_function_declaration_in_reverse_order([&](FunctionDeclaration const& function) { // 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) if (function_names.set(function.name()) == AK::HashSetResult::InsertedNewEntry)
functions_to_initialize.append(function); functions_to_initialize.append(function);
}); }));
auto const& arguments_name = vm.names.arguments.as_string(); auto const& arguments_name = vm.names.arguments.as_string();
@ -385,10 +387,11 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
arguments_object_needed = false; arguments_object_needed = false;
if (!has_parameter_expressions && arguments_object_needed) { if (!has_parameter_expressions && arguments_object_needed) {
scope_body->for_each_lexically_declared_name([&](auto const& name) { // NOTE: Nothing in the callback throws an exception.
MUST(scope_body->for_each_lexically_declared_name([&](auto const& name) {
if (name == arguments_name) if (name == arguments_name)
arguments_object_needed = false; arguments_object_needed = false;
}); }));
} }
} else { } else {
arguments_object_needed = false; arguments_object_needed = false;
@ -489,12 +492,14 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
if (!has_parameter_expressions) { if (!has_parameter_expressions) {
if (scope_body) { if (scope_body) {
scope_body->for_each_var_declared_name([&](auto const& name) { // NOTE: Due to the use of MUST with `create_mutable_binding` and `initialize_binding` below,
// an exception should not result from `for_each_var_declared_name`.
MUST(scope_body->for_each_var_declared_name([&](auto const& name) {
if (!parameter_names.contains(name) && instantiated_var_names.set(name) == AK::HashSetResult::InsertedNewEntry) { if (!parameter_names.contains(name) && instantiated_var_names.set(name) == AK::HashSetResult::InsertedNewEntry) {
MUST(environment->create_mutable_binding(vm, name, false)); MUST(environment->create_mutable_binding(vm, name, false));
MUST(environment->initialize_binding(vm, name, js_undefined(), Environment::InitializeBindingHint::Normal)); MUST(environment->initialize_binding(vm, name, js_undefined(), Environment::InitializeBindingHint::Normal));
} }
}); }));
} }
var_environment = environment; var_environment = environment;
} else { } else {
@ -502,7 +507,9 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
callee_context.variable_environment = var_environment; callee_context.variable_environment = var_environment;
if (scope_body) { if (scope_body) {
scope_body->for_each_var_declared_name([&](auto const& name) { // NOTE: Due to the use of MUST with `create_mutable_binding`, `get_binding_value` and `initialize_binding` below,
// an exception should not result from `for_each_var_declared_name`.
MUST(scope_body->for_each_var_declared_name([&](auto const& name) {
if (instantiated_var_names.set(name) != AK::HashSetResult::InsertedNewEntry) if (instantiated_var_names.set(name) != AK::HashSetResult::InsertedNewEntry)
return; return;
MUST(var_environment->create_mutable_binding(vm, name, false)); MUST(var_environment->create_mutable_binding(vm, name, false));
@ -514,13 +521,15 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
initial_value = MUST(environment->get_binding_value(vm, name, false)); initial_value = MUST(environment->get_binding_value(vm, name, false));
MUST(var_environment->initialize_binding(vm, name, initial_value, Environment::InitializeBindingHint::Normal)); MUST(var_environment->initialize_binding(vm, name, initial_value, Environment::InitializeBindingHint::Normal));
}); }));
} }
} }
// B.3.2.1 Changes to FunctionDeclarationInstantiation, https://tc39.es/ecma262/#sec-web-compat-functiondeclarationinstantiation // B.3.2.1 Changes to FunctionDeclarationInstantiation, https://tc39.es/ecma262/#sec-web-compat-functiondeclarationinstantiation
if (!m_strict && scope_body) { if (!m_strict && scope_body) {
scope_body->for_each_function_hoistable_with_annexB_extension([&](FunctionDeclaration& function_declaration) { // 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(); auto& function_name = function_declaration.name();
if (parameter_names.contains(function_name)) if (parameter_names.contains(function_name))
return; return;
@ -532,7 +541,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
} }
function_declaration.set_should_do_additional_annexB_steps(); function_declaration.set_should_do_additional_annexB_steps();
}); }));
} }
GCPtr<Environment> lex_environment; GCPtr<Environment> lex_environment;
@ -565,14 +574,17 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
return {}; return {};
if (!Bytecode::Interpreter::current()) { if (!Bytecode::Interpreter::current()) {
scope_body->for_each_lexically_scoped_declaration([&](Declaration const& declaration) { // NOTE: Due to the use of MUST in the callback, an exception should not result from `for_each_lexically_scoped_declaration`.
declaration.for_each_bound_name([&](auto const& name) { MUST(scope_body->for_each_lexically_scoped_declaration([&](Declaration const& declaration) {
// NOTE: Due to the use of MUST with `create_immutable_binding` and `create_mutable_binding` below,
// an exception should not result from `for_each_bound_name`.
MUST(declaration.for_each_bound_name([&](auto const& name) {
if (declaration.is_constant_declaration()) if (declaration.is_constant_declaration())
MUST(lex_environment->create_immutable_binding(vm, name, true)); MUST(lex_environment->create_immutable_binding(vm, name, true));
else else
MUST(lex_environment->create_mutable_binding(vm, name, false)); MUST(lex_environment->create_mutable_binding(vm, name, false));
}); }));
}); }));
} }
auto* private_environment = callee_context.private_environment; auto* private_environment = callee_context.private_environment;

View file

@ -436,7 +436,9 @@ ThrowCompletionOr<void> SourceTextModule::initialize_environment(VM& vm)
// 21. For each element d of varDeclarations, do // 21. For each element d of varDeclarations, do
// a. For each element dn of the BoundNames of d, do // a. For each element dn of the BoundNames of d, do
m_ecmascript_code->for_each_var_declared_name([&](auto const& name) { // NOTE: Due to the use of MUST with `create_mutable_binding` and `initialize_binding` below,
// an exception should not result from `for_each_var_declared_name`.
MUST(m_ecmascript_code->for_each_var_declared_name([&](auto const& name) {
// i. If dn is not an element of declaredVarNames, then // i. If dn is not an element of declaredVarNames, then
if (!declared_var_names.contains_slow(name)) { if (!declared_var_names.contains_slow(name)) {
// 1. Perform ! env.CreateMutableBinding(dn, false). // 1. Perform ! env.CreateMutableBinding(dn, false).
@ -448,7 +450,7 @@ ThrowCompletionOr<void> SourceTextModule::initialize_environment(VM& vm)
// 3. Append dn to declaredVarNames. // 3. Append dn to declaredVarNames.
declared_var_names.empend(name); declared_var_names.empend(name);
} }
}); }));
// 22. Let lexDeclarations be the LexicallyScopedDeclarations of code. // 22. Let lexDeclarations be the LexicallyScopedDeclarations of code.
// Note: We only loop through them in step 24. // Note: We only loop through them in step 24.
@ -457,9 +459,12 @@ ThrowCompletionOr<void> SourceTextModule::initialize_environment(VM& vm)
PrivateEnvironment* private_environment = nullptr; PrivateEnvironment* private_environment = nullptr;
// 24. For each element d of lexDeclarations, do // 24. For each element d of lexDeclarations, do
m_ecmascript_code->for_each_lexically_scoped_declaration([&](Declaration const& declaration) { // NOTE: Due to the use of MUST in the callback, an exception should not result from `for_each_lexically_scoped_declaration`.
MUST(m_ecmascript_code->for_each_lexically_scoped_declaration([&](Declaration const& declaration) {
// a. For each element dn of the BoundNames of d, do // a. For each element dn of the BoundNames of d, do
declaration.for_each_bound_name([&](DeprecatedFlyString const& name) { // NOTE: Due to the use of MUST with `create_immutable_binding`, `create_mutable_binding` and `initialize_binding` below,
// an exception should not result from `for_each_bound_name`.
MUST(declaration.for_each_bound_name([&](DeprecatedFlyString const& name) {
// i. If IsConstantDeclaration of d is true, then // i. If IsConstantDeclaration of d is true, then
if (declaration.is_constant_declaration()) { if (declaration.is_constant_declaration()) {
// 1. Perform ! env.CreateImmutableBinding(dn, true). // 1. Perform ! env.CreateImmutableBinding(dn, true).
@ -487,8 +492,8 @@ ThrowCompletionOr<void> SourceTextModule::initialize_environment(VM& vm)
// 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));
} }
}); }));
}); }));
// Note: The default export name is also part of the local lexical declarations but // Note: The default export name is also part of the local lexical declarations but
// instead of making that a special case in the parser we just check it here. // instead of making that a special case in the parser we just check it here.