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

LibJS: Use Identifier to represent FunctionParameter name

Using identifier instead of string allows to store supplemental
information about whether it can be represented as local variable.
This commit is contained in:
Aliaksandr Kalenik 2023-07-06 17:49:38 +02:00 committed by Andreas Kling
parent 2f1d6c0b9a
commit 2e81cc4cf7
5 changed files with 50 additions and 31 deletions

View file

@ -2537,11 +2537,14 @@ void FunctionNode::dump(int indent, DeprecatedString const& class_name) const
for (auto& parameter : m_parameters) { for (auto& parameter : m_parameters) {
parameter.binding.visit( parameter.binding.visit(
[&](DeprecatedFlyString const& name) { [&](Identifier const& identifier) {
print_indent(indent + 2); if (parameter.is_rest) {
if (parameter.is_rest) print_indent(indent + 2);
out("..."); out("...");
outln("{}", name); identifier.dump(0);
} else {
identifier.dump(indent + 2);
}
}, },
[&](BindingPattern const& pattern) { [&](BindingPattern const& pattern) {
pattern.dump(indent + 2); pattern.dump(indent + 2);
@ -4479,6 +4482,16 @@ ThrowCompletionOr<void> ScopeNode::for_each_lexically_declared_name(ThrowComplet
return {}; return {};
} }
ThrowCompletionOr<void> ScopeNode::for_each_lexically_declared_identifier(ThrowCompletionOrVoidCallback<Identifier const&>&& callback) const
{
for (auto const& declaration : m_lexical_declarations) {
TRY(declaration->for_each_bound_identifier([&](auto const& identifier) {
return callback(identifier);
}));
}
return {};
}
ThrowCompletionOr<void> ScopeNode::for_each_var_declared_name(ThrowCompletionOrVoidCallback<DeprecatedFlyString const&>&& callback) const ThrowCompletionOr<void> ScopeNode::for_each_var_declared_name(ThrowCompletionOrVoidCallback<DeprecatedFlyString const&>&& callback) const
{ {
for (auto& declaration : m_var_declarations) { for (auto& declaration : m_var_declarations) {
@ -4770,7 +4783,9 @@ ThrowCompletionOr<void> Program::global_declaration_instantiation(VM& vm, Global
// 1. Let lexNames be the LexicallyDeclaredNames of script. // 1. Let lexNames be the LexicallyDeclaredNames of script.
// 2. Let varNames be the VarDeclaredNames of script. // 2. Let varNames be the VarDeclaredNames of script.
// 3. For each element name of lexNames, do // 3. For each element name of lexNames, do
TRY(for_each_lexically_declared_name([&](DeprecatedFlyString const& name) -> ThrowCompletionOr<void> { TRY(for_each_lexically_declared_identifier([&](Identifier const& identifier) -> ThrowCompletionOr<void> {
auto const& name = identifier.string();
// a. If env.HasVarDeclaration(name) is true, throw a SyntaxError exception. // a. If env.HasVarDeclaration(name) is true, throw a SyntaxError exception.
if (global_environment.has_var_declaration(name)) if (global_environment.has_var_declaration(name))
return vm.throw_completion<SyntaxError>(ErrorType::TopLevelVariableAlreadyDeclared, name); return vm.throw_completion<SyntaxError>(ErrorType::TopLevelVariableAlreadyDeclared, name);

View file

@ -308,6 +308,7 @@ public:
ThrowCompletionOr<void> for_each_lexically_scoped_declaration(ThrowCompletionOrVoidCallback<Declaration const&>&& callback) const; ThrowCompletionOr<void> for_each_lexically_scoped_declaration(ThrowCompletionOrVoidCallback<Declaration const&>&& callback) const;
ThrowCompletionOr<void> for_each_lexically_declared_name(ThrowCompletionOrVoidCallback<DeprecatedFlyString const&>&& callback) const; ThrowCompletionOr<void> for_each_lexically_declared_name(ThrowCompletionOrVoidCallback<DeprecatedFlyString const&>&& callback) const;
ThrowCompletionOr<void> for_each_lexically_declared_identifier(ThrowCompletionOrVoidCallback<Identifier const&>&& callback) const;
ThrowCompletionOr<void> for_each_var_declared_name(ThrowCompletionOrVoidCallback<DeprecatedFlyString const&>&& callback) const; ThrowCompletionOr<void> for_each_var_declared_name(ThrowCompletionOrVoidCallback<DeprecatedFlyString const&>&& callback) const;
ThrowCompletionOr<void> for_each_var_declared_identifier(ThrowCompletionOrVoidCallback<Identifier const&>&& callback) const; ThrowCompletionOr<void> for_each_var_declared_identifier(ThrowCompletionOrVoidCallback<Identifier const&>&& callback) const;
@ -665,12 +666,6 @@ struct BindingPattern : RefCounted<BindingPattern> {
Kind kind { Kind::Object }; Kind kind { Kind::Object };
}; };
struct FunctionParameter {
Variant<DeprecatedFlyString, NonnullRefPtr<BindingPattern const>> binding;
RefPtr<Expression const> default_value;
bool is_rest { false };
};
class Identifier final : public Expression { class Identifier final : public Expression {
public: public:
explicit Identifier(SourceRange source_range, DeprecatedFlyString string) explicit Identifier(SourceRange source_range, DeprecatedFlyString string)
@ -703,6 +698,12 @@ private:
Optional<size_t> m_local_variable_index; Optional<size_t> m_local_variable_index;
}; };
struct FunctionParameter {
Variant<NonnullRefPtr<Identifier const>, NonnullRefPtr<BindingPattern const>> binding;
RefPtr<Expression const> default_value;
bool is_rest { false };
};
class FunctionNode { class FunctionNode {
public: public:
StringView name() const { return m_name ? m_name->string().view() : ""sv; } StringView name() const { return m_name ? m_name->string().view() : ""sv; }

View file

@ -79,8 +79,8 @@ public:
scope_pusher.m_function_parameters = parameters; scope_pusher.m_function_parameters = parameters;
for (auto& parameter : parameters) { for (auto& parameter : parameters) {
parameter.binding.visit( parameter.binding.visit(
[&](DeprecatedFlyString const& name) { [&](Identifier const& identifier) {
scope_pusher.m_forbidden_lexical_names.set(name); scope_pusher.m_forbidden_lexical_names.set(identifier.string());
}, },
[&](NonnullRefPtr<BindingPattern const> const& binding_pattern) { [&](NonnullRefPtr<BindingPattern const> const& binding_pattern) {
// NOTE: Nothing in the callback throws an exception. // NOTE: Nothing in the callback throws an exception.
@ -864,7 +864,7 @@ static bool is_strict_reserved_word(StringView str)
static bool is_simple_parameter_list(Vector<FunctionParameter> const& parameters) static bool is_simple_parameter_list(Vector<FunctionParameter> const& parameters)
{ {
return all_of(parameters, [](FunctionParameter const& parameter) { return all_of(parameters, [](FunctionParameter const& parameter) {
return !parameter.is_rest && parameter.default_value.is_null() && parameter.binding.has<DeprecatedFlyString>(); return !parameter.is_rest && parameter.default_value.is_null() && parameter.binding.has<NonnullRefPtr<Identifier const>>();
}); });
} }
@ -939,7 +939,8 @@ RefPtr<FunctionExpression const> Parser::try_parse_arrow_function_expression(boo
syntax_error("BindingIdentifier may not be 'arguments' or 'eval' in strict mode"); syntax_error("BindingIdentifier may not be 'arguments' or 'eval' in strict mode");
if (is_async && token.value() == "await"sv) if (is_async && token.value() == "await"sv)
syntax_error("'await' is a reserved identifier in async functions"); syntax_error("'await' is a reserved identifier in async functions");
parameters.append({ DeprecatedFlyString { token.value() }, {} }); auto identifier = create_ast_node<Identifier const>({ m_source_code, rule_start.position(), position() }, token.DeprecatedFlyString_value());
parameters.append({ identifier, {} });
} }
// If there's a newline between the closing paren and arrow it's not a valid arrow function, // If there's a newline between the closing paren and arrow it's not a valid arrow function,
// ASI should kick in instead (it'll then fail with "Unexpected token Arrow") // ASI should kick in instead (it'll then fail with "Unexpected token Arrow")
@ -1003,8 +1004,8 @@ RefPtr<FunctionExpression const> Parser::try_parse_arrow_function_expression(boo
if (body->in_strict_mode()) { if (body->in_strict_mode()) {
for (auto& parameter : parameters) { for (auto& parameter : parameters) {
parameter.binding.visit( parameter.binding.visit(
[&](DeprecatedFlyString const& name) { [&](Identifier const& identifier) {
check_identifier_name_for_assignment_validity(name, true); check_identifier_name_for_assignment_validity(identifier.string(), true);
}, },
[&](auto const&) {}); [&](auto const&) {});
} }
@ -1495,7 +1496,7 @@ NonnullRefPtr<ClassExpression const> Parser::parse_class_expression(bool expect_
// this function does not. // this function does not.
// So we use a custom version of SuperCall which doesn't use the @@iterator // So we use a custom version of SuperCall which doesn't use the @@iterator
// method on %Array.prototype% visibly. // method on %Array.prototype% visibly.
DeprecatedFlyString argument_name = "args"; auto argument_name = create_ast_node<Identifier const>({ m_source_code, rule_start.position(), position() }, "args");
auto super_call = create_ast_node<SuperCall>( auto super_call = create_ast_node<SuperCall>(
{ m_source_code, rule_start.position(), position() }, { m_source_code, rule_start.position(), position() },
SuperCall::IsPartOfSyntheticConstructor::Yes, SuperCall::IsPartOfSyntheticConstructor::Yes,
@ -2675,7 +2676,9 @@ NonnullRefPtr<FunctionBody const> Parser::parse_function_body(Vector<FunctionPar
Vector<StringView> parameter_names; Vector<StringView> parameter_names;
for (auto& parameter : parameters) { for (auto& parameter : parameters) {
parameter.binding.visit( parameter.binding.visit(
[&](DeprecatedFlyString const& parameter_name) { [&](Identifier const& identifier) {
auto const& parameter_name = identifier.string();
check_identifier_name_for_assignment_validity(parameter_name, function_body->in_strict_mode()); check_identifier_name_for_assignment_validity(parameter_name, function_body->in_strict_mode());
if (function_kind == FunctionKind::Generator && parameter_name == "yield"sv) if (function_kind == FunctionKind::Generator && parameter_name == "yield"sv)
syntax_error("Parameter name 'yield' not allowed in this context"); syntax_error("Parameter name 'yield' not allowed in this context");
@ -2842,7 +2845,7 @@ Vector<FunctionParameter> Parser::parse_formal_parameters(int& function_length,
Vector<FunctionParameter> parameters; Vector<FunctionParameter> parameters;
auto consume_identifier_or_binding_pattern = [&]() -> Variant<DeprecatedFlyString, NonnullRefPtr<BindingPattern const>> { auto consume_identifier_or_binding_pattern = [&]() -> Variant<NonnullRefPtr<Identifier const>, NonnullRefPtr<BindingPattern const>> {
if (auto pattern = parse_binding_pattern(AllowDuplicates::No, AllowMemberExpressions::No)) if (auto pattern = parse_binding_pattern(AllowDuplicates::No, AllowMemberExpressions::No))
return pattern.release_nonnull(); return pattern.release_nonnull();
@ -2853,8 +2856,8 @@ Vector<FunctionParameter> Parser::parse_formal_parameters(int& function_length,
for (auto& parameter : parameters) { for (auto& parameter : parameters) {
bool has_same_name = parameter.binding.visit( bool has_same_name = parameter.binding.visit(
[&](DeprecatedFlyString const& name) { [&](Identifier const& identifier) {
return name == parameter_name; return identifier.string() == parameter_name;
}, },
[&](NonnullRefPtr<BindingPattern const> const& bindings) { [&](NonnullRefPtr<BindingPattern const> const& bindings) {
bool found_duplicate = false; bool found_duplicate = false;
@ -2882,7 +2885,7 @@ Vector<FunctionParameter> Parser::parse_formal_parameters(int& function_length,
syntax_error(message, Position { token.line_number(), token.line_column() }); syntax_error(message, Position { token.line_number(), token.line_column() });
break; break;
} }
return DeprecatedFlyString { token.value() }; return create_ast_node<Identifier const>({ m_source_code, rule_start.position(), position() }, token.DeprecatedFlyString_value());
}; };
while (match(TokenType::CurlyOpen) || match(TokenType::BracketOpen) || match_identifier() || match(TokenType::TripleDot)) { while (match(TokenType::CurlyOpen) || match(TokenType::BracketOpen) || match_identifier() || match(TokenType::TripleDot)) {

View file

@ -1128,7 +1128,7 @@ Object* create_mapped_arguments_object(VM& vm, FunctionObject& function, Vector<
VERIFY(formals.size() <= NumericLimits<i32>::max()); VERIFY(formals.size() <= NumericLimits<i32>::max());
for (i32 index = static_cast<i32>(formals.size()) - 1; index >= 0; --index) { for (i32 index = static_cast<i32>(formals.size()) - 1; index >= 0; --index) {
// a. Let name be parameterNames[index]. // a. Let name be parameterNames[index].
auto const& name = formals[index].binding.get<DeprecatedFlyString>(); auto const& name = formals[index].binding.get<NonnullRefPtr<Identifier const>>()->string();
// b. If name is not an element of mappedNames, then // b. If name is not an element of mappedNames, then
if (mapped_names.contains(name)) if (mapped_names.contains(name))

View file

@ -94,7 +94,7 @@ ECMAScriptFunctionObject::ECMAScriptFunctionObject(DeprecatedFlyString name, Dep
return false; return false;
if (parameter.default_value) if (parameter.default_value)
return false; return false;
if (!parameter.binding.template has<DeprecatedFlyString>()) if (!parameter.binding.template has<NonnullRefPtr<Identifier const>>())
return false; return false;
return true; return true;
}); });
@ -353,8 +353,8 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
has_parameter_expressions = true; has_parameter_expressions = true;
parameter.binding.visit( parameter.binding.visit(
[&](DeprecatedFlyString const& name) { [&](Identifier const& identifier) {
if (parameter_names.set(name) != AK::HashSetResult::InsertedNewEntry) if (parameter_names.set(identifier.string()) != AK::HashSetResult::InsertedNewEntry)
has_duplicates = true; has_duplicates = true;
}, },
[&](NonnullRefPtr<BindingPattern const> const& pattern) { [&](NonnullRefPtr<BindingPattern const> const& pattern) {
@ -362,8 +362,8 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
has_parameter_expressions = true; has_parameter_expressions = true;
// NOTE: Nothing in the callback throws an exception. // NOTE: Nothing in the callback throws an exception.
MUST(pattern->for_each_bound_name([&](auto& name) { MUST(pattern->for_each_bound_identifier([&](auto& identifier) {
if (parameter_names.set(name) != AK::HashSetResult::InsertedNewEntry) if (parameter_names.set(identifier.string()) != AK::HashSetResult::InsertedNewEntry)
has_duplicates = true; has_duplicates = true;
})); }));
}); });
@ -478,8 +478,8 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
Environment* used_environment = has_duplicates ? nullptr : environment; Environment* used_environment = has_duplicates ? nullptr : environment;
if constexpr (IsSame<DeprecatedFlyString const&, decltype(param)>) { if constexpr (IsSame<NonnullRefPtr<Identifier const> const&, decltype(param)>) {
Reference reference = TRY(vm.resolve_binding(param, used_environment)); Reference reference = TRY(vm.resolve_binding(param->string(), used_environment));
// Here the difference from hasDuplicates is important // Here the difference from hasDuplicates is important
if (has_duplicates) if (has_duplicates)
return reference.put_value(vm, argument_value); return reference.put_value(vm, argument_value);