LibJS: Update parser to detect if identifier refer a "local" variable
This modification enables the parser to determine whether an identifier used within a function refers to a local variable or not. In this context, a local identifier means that it is not captured by any nested function declaration which means it modified only from inside a function. The information about whether identifier is local is stored inside Identifier AST node and also contains information about the index of local variable inside a function and information about total number of local variables used by a function is stored in function nodes.
This commit is contained in:
parent
c734f2b5e6
commit
380abddf3c
Notes:
sideshowbarker
2024-07-17 18:23:22 +09:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/SerenityOS/serenity/commit/380abddf3c Pull-request: https://github.com/SerenityOS/serenity/pull/19719 Reviewed-by: https://github.com/awesomekling
4 changed files with 270 additions and 99 deletions
|
@ -2679,7 +2679,11 @@ Completion Identifier::execute(Interpreter& interpreter) const
|
|||
void Identifier::dump(int indent) const
|
||||
{
|
||||
print_indent(indent);
|
||||
outln("Identifier \"{}\"", m_string);
|
||||
if (is_local()) {
|
||||
outln("Identifier \"{}\" is_local=(true) index=({})", m_string, m_local_variable_index);
|
||||
} else {
|
||||
outln("Identifier \"{}\"", m_string);
|
||||
}
|
||||
}
|
||||
|
||||
Completion PrivateIdentifier::execute(Interpreter&) const
|
||||
|
@ -4435,6 +4439,16 @@ ThrowCompletionOr<void> ScopeNode::for_each_var_function_declaration_in_reverse_
|
|||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> ScopeNode::for_each_lexical_function_declaration_in_reverse_order(ThrowCompletionOrVoidCallback<FunctionDeclaration const&>&& callback) const
|
||||
{
|
||||
for (ssize_t i = m_lexical_declarations.size() - 1; i >= 0; i--) {
|
||||
auto& declaration = m_lexical_declarations[i];
|
||||
if (is<FunctionDeclaration>(declaration))
|
||||
TRY(callback(static_cast<FunctionDeclaration const&>(*declaration)));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> ScopeNode::for_each_var_scoped_variable_declaration(ThrowCompletionOrVoidCallback<VariableDeclaration const&>&& callback) const
|
||||
{
|
||||
for (auto& declaration : m_var_declarations) {
|
||||
|
|
|
@ -312,12 +312,21 @@ public:
|
|||
ThrowCompletionOr<void> for_each_var_declared_name(ThrowCompletionOrVoidCallback<DeprecatedFlyString const&>&& callback) const;
|
||||
|
||||
ThrowCompletionOr<void> for_each_var_function_declaration_in_reverse_order(ThrowCompletionOrVoidCallback<FunctionDeclaration const&>&& callback) const;
|
||||
ThrowCompletionOr<void> for_each_lexical_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(VM&, Environment*) const;
|
||||
|
||||
ThrowCompletionOr<void> for_each_function_hoistable_with_annexB_extension(ThrowCompletionOrVoidCallback<FunctionDeclaration&>&& callback) const;
|
||||
|
||||
Vector<DeprecatedFlyString> const& local_variables_names() const { return m_local_variables_names; }
|
||||
size_t add_local_variable(DeprecatedFlyString name)
|
||||
{
|
||||
auto index = m_local_variables_names.size();
|
||||
m_local_variables_names.append(name);
|
||||
return index;
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit ScopeNode(SourceRange source_range)
|
||||
: Statement(source_range)
|
||||
|
@ -332,6 +341,8 @@ private:
|
|||
Vector<NonnullRefPtr<Declaration const>> m_var_declarations;
|
||||
|
||||
Vector<NonnullRefPtr<FunctionDeclaration const>> m_functions_hoistable_with_annexB_extension;
|
||||
|
||||
Vector<DeprecatedFlyString> m_local_variables_names;
|
||||
};
|
||||
|
||||
// ImportEntry Record, https://tc39.es/ecma262/#table-importentry-record-fields
|
||||
|
@ -659,6 +670,7 @@ public:
|
|||
Statement const& body() const { return *m_body; }
|
||||
Vector<FunctionParameter> const& parameters() const { return m_parameters; };
|
||||
i32 function_length() const { return m_function_length; }
|
||||
Vector<DeprecatedFlyString> const& local_variables_names() const { return m_local_variables_names; }
|
||||
bool is_strict_mode() const { return m_is_strict_mode; }
|
||||
bool might_need_arguments_object() const { return m_might_need_arguments_object; }
|
||||
bool contains_direct_call_to_eval() const { return m_contains_direct_call_to_eval; }
|
||||
|
@ -666,7 +678,7 @@ public:
|
|||
FunctionKind kind() const { return m_kind; }
|
||||
|
||||
protected:
|
||||
FunctionNode(DeprecatedFlyString name, DeprecatedString source_text, NonnullRefPtr<Statement const> body, Vector<FunctionParameter> parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function)
|
||||
FunctionNode(RefPtr<Identifier const> name, DeprecatedString source_text, NonnullRefPtr<Statement const> body, Vector<FunctionParameter> parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function, Vector<DeprecatedFlyString> local_variables_names)
|
||||
: m_name(move(name))
|
||||
, m_source_text(move(source_text))
|
||||
, m_body(move(body))
|
||||
|
@ -677,6 +689,7 @@ protected:
|
|||
, m_might_need_arguments_object(might_need_arguments_object)
|
||||
, m_contains_direct_call_to_eval(contains_direct_call_to_eval)
|
||||
, m_is_arrow_function(is_arrow_function)
|
||||
, m_local_variables_names(local_variables_names)
|
||||
{
|
||||
if (m_is_arrow_function)
|
||||
VERIFY(!m_might_need_arguments_object);
|
||||
|
@ -695,6 +708,8 @@ private:
|
|||
bool m_might_need_arguments_object : 1 { false };
|
||||
bool m_contains_direct_call_to_eval : 1 { false };
|
||||
bool m_is_arrow_function : 1 { false };
|
||||
|
||||
Vector<DeprecatedFlyString> m_local_variables_names;
|
||||
};
|
||||
|
||||
class FunctionDeclaration final
|
||||
|
@ -703,9 +718,9 @@ class FunctionDeclaration final
|
|||
public:
|
||||
static bool must_have_name() { return true; }
|
||||
|
||||
FunctionDeclaration(SourceRange source_range, DeprecatedFlyString const& name, DeprecatedString source_text, NonnullRefPtr<Statement const> body, Vector<FunctionParameter> parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval)
|
||||
FunctionDeclaration(SourceRange source_range, RefPtr<Identifier const> name, DeprecatedString source_text, NonnullRefPtr<Statement const> body, Vector<FunctionParameter> parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, Vector<DeprecatedFlyString> local_variables_names)
|
||||
: Declaration(source_range)
|
||||
, FunctionNode(name, move(source_text), move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, contains_direct_call_to_eval, false)
|
||||
, FunctionNode(name, move(source_text), move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, contains_direct_call_to_eval, false, move(local_variables_names))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -729,9 +744,9 @@ class FunctionExpression final
|
|||
public:
|
||||
static bool must_have_name() { return false; }
|
||||
|
||||
FunctionExpression(SourceRange source_range, DeprecatedFlyString const& name, DeprecatedString source_text, NonnullRefPtr<Statement const> body, Vector<FunctionParameter> parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function = false)
|
||||
FunctionExpression(SourceRange source_range, RefPtr<Identifier const> name, DeprecatedString source_text, NonnullRefPtr<Statement const> body, Vector<FunctionParameter> parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, Vector<DeprecatedFlyString> local_variables_names, bool is_arrow_function = false)
|
||||
: Expression(source_range)
|
||||
, FunctionNode(name, move(source_text), move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, contains_direct_call_to_eval, is_arrow_function)
|
||||
, FunctionNode(name, move(source_text), move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, contains_direct_call_to_eval, is_arrow_function, move(local_variables_names))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1254,6 +1269,14 @@ public:
|
|||
|
||||
DeprecatedFlyString const& string() const { return m_string; }
|
||||
|
||||
bool is_local() const { return m_local_variable_index.has_value(); }
|
||||
size_t local_variable_index() const
|
||||
{
|
||||
VERIFY(m_local_variable_index.has_value());
|
||||
return m_local_variable_index.value();
|
||||
}
|
||||
void set_local_variable_index(size_t index) { m_local_variable_index = index; }
|
||||
|
||||
virtual Completion execute(Interpreter&) const override;
|
||||
virtual void dump(int indent) const override;
|
||||
virtual ThrowCompletionOr<Reference> to_reference(Interpreter&) const override;
|
||||
|
@ -1264,6 +1287,8 @@ private:
|
|||
|
||||
DeprecatedFlyString m_string;
|
||||
mutable EnvironmentCoordinate m_cached_environment_coordinate;
|
||||
|
||||
Optional<size_t> m_local_variable_index;
|
||||
};
|
||||
|
||||
class PrivateIdentifier final : public Expression {
|
||||
|
|
|
@ -35,10 +35,23 @@ class ScopePusher {
|
|||
StaticInitTopLevel
|
||||
};
|
||||
|
||||
enum class ScopeType {
|
||||
Function,
|
||||
Program,
|
||||
Block,
|
||||
ForLoop,
|
||||
With,
|
||||
Catch,
|
||||
ClassStaticInit,
|
||||
ClassField,
|
||||
ClassDeclaration,
|
||||
};
|
||||
|
||||
private:
|
||||
ScopePusher(Parser& parser, ScopeNode* node, ScopeLevel scope_level)
|
||||
ScopePusher(Parser& parser, ScopeNode* node, ScopeLevel scope_level, ScopeType type)
|
||||
: m_parser(parser)
|
||||
, m_scope_level(scope_level)
|
||||
, m_type(type)
|
||||
{
|
||||
m_parent_scope = exchange(m_parser.m_state.current_scope_pusher, this);
|
||||
VERIFY(node || (m_parent_scope && scope_level == ScopeLevel::NotTopLevel));
|
||||
|
@ -62,7 +75,7 @@ private:
|
|||
public:
|
||||
static ScopePusher function_scope(Parser& parser, FunctionBody& function_body, Vector<FunctionParameter> const& parameters)
|
||||
{
|
||||
ScopePusher scope_pusher(parser, &function_body, ScopeLevel::FunctionTopLevel);
|
||||
ScopePusher scope_pusher(parser, &function_body, ScopeLevel::FunctionTopLevel, ScopeType::Function);
|
||||
scope_pusher.m_function_parameters = parameters;
|
||||
for (auto& parameter : parameters) {
|
||||
parameter.binding.visit(
|
||||
|
@ -81,52 +94,61 @@ public:
|
|||
|
||||
static ScopePusher program_scope(Parser& parser, Program& program)
|
||||
{
|
||||
return ScopePusher(parser, &program, program.type() == Program::Type::Script ? ScopeLevel::ScriptTopLevel : ScopeLevel::ModuleTopLevel);
|
||||
return ScopePusher(parser, &program, program.type() == Program::Type::Script ? ScopeLevel::ScriptTopLevel : ScopeLevel::ModuleTopLevel, ScopeType::Program);
|
||||
}
|
||||
|
||||
static ScopePusher block_scope(Parser& parser, ScopeNode& node)
|
||||
{
|
||||
return ScopePusher(parser, &node, ScopeLevel::NotTopLevel);
|
||||
return ScopePusher(parser, &node, ScopeLevel::NotTopLevel, ScopeType::Block);
|
||||
}
|
||||
|
||||
static ScopePusher for_loop_scope(Parser& parser, RefPtr<ASTNode const> const& init)
|
||||
static ScopePusher for_loop_scope(Parser& parser, ScopeNode& node)
|
||||
{
|
||||
ScopePusher scope_pusher(parser, nullptr, ScopeLevel::NotTopLevel);
|
||||
if (init && is<VariableDeclaration>(*init)) {
|
||||
auto& variable_declaration = static_cast<VariableDeclaration const&>(*init);
|
||||
if (variable_declaration.declaration_kind() != DeclarationKind::Var) {
|
||||
// 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);
|
||||
}));
|
||||
}
|
||||
}
|
||||
return ScopePusher(parser, &node, ScopeLevel::NotTopLevel, ScopeType::ForLoop);
|
||||
}
|
||||
|
||||
static ScopePusher with_scope(Parser& parser, ScopeNode& node)
|
||||
{
|
||||
ScopePusher scope_pusher(parser, &node, ScopeLevel::NotTopLevel, ScopeType::With);
|
||||
return scope_pusher;
|
||||
}
|
||||
|
||||
static ScopePusher catch_scope(Parser& parser, RefPtr<BindingPattern const> const& pattern, DeprecatedFlyString const& parameter)
|
||||
{
|
||||
ScopePusher scope_pusher(parser, nullptr, ScopeLevel::NotTopLevel);
|
||||
ScopePusher scope_pusher(parser, nullptr, ScopeLevel::NotTopLevel, ScopeType::Catch);
|
||||
if (pattern) {
|
||||
// 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_bound_names.set(name);
|
||||
}));
|
||||
} else if (!parameter.is_empty()) {
|
||||
scope_pusher.m_var_names.set(parameter);
|
||||
scope_pusher.m_bound_names.set(parameter);
|
||||
}
|
||||
|
||||
return scope_pusher;
|
||||
}
|
||||
|
||||
static ScopePusher static_init_block_scope(Parser& parser, ScopeNode& node)
|
||||
{
|
||||
return ScopePusher(parser, &node, ScopeLevel::StaticInitTopLevel);
|
||||
ScopePusher scope_pusher(parser, &node, ScopeLevel::StaticInitTopLevel, ScopeType::ClassStaticInit);
|
||||
return scope_pusher;
|
||||
}
|
||||
|
||||
static ScopePusher class_field_scope(Parser& parser)
|
||||
static ScopePusher class_field_scope(Parser& parser, ScopeNode& node)
|
||||
{
|
||||
return ScopePusher(parser, nullptr, ScopeLevel::NotTopLevel);
|
||||
ScopePusher scope_pusher(parser, &node, ScopeLevel::NotTopLevel, ScopeType::ClassField);
|
||||
return scope_pusher;
|
||||
}
|
||||
|
||||
static ScopePusher class_declaration_scope(Parser& parser, RefPtr<Identifier const> class_name)
|
||||
{
|
||||
ScopePusher scope_pusher(parser, nullptr, ScopeLevel::NotTopLevel, ScopeType::ClassDeclaration);
|
||||
if (class_name) {
|
||||
scope_pusher.m_bound_names.set(class_name->string());
|
||||
}
|
||||
return scope_pusher;
|
||||
}
|
||||
|
||||
void add_declaration(NonnullRefPtr<Declaration const> declaration)
|
||||
|
@ -176,7 +198,7 @@ public:
|
|||
} else {
|
||||
VERIFY(is<FunctionDeclaration>(*declaration));
|
||||
auto& function_declaration = static_cast<FunctionDeclaration const&>(*declaration);
|
||||
auto& function_name = function_declaration.name();
|
||||
auto function_name = function_declaration.name();
|
||||
if (m_var_names.contains(function_name) || m_lexical_names.contains(function_name))
|
||||
throw_identifier_declared(function_name, declaration);
|
||||
|
||||
|
@ -222,7 +244,11 @@ public:
|
|||
|
||||
bool contains_direct_call_to_eval() const { return m_contains_direct_call_to_eval; }
|
||||
bool contains_access_to_arguments_object() const { return m_contains_access_to_arguments_object; }
|
||||
void set_contains_direct_call_to_eval() { m_contains_direct_call_to_eval = true; }
|
||||
void set_contains_direct_call_to_eval()
|
||||
{
|
||||
m_contains_direct_call_to_eval = true;
|
||||
m_can_use_local_variables = false;
|
||||
}
|
||||
void set_contains_access_to_arguments_object() { m_contains_access_to_arguments_object = true; }
|
||||
|
||||
~ScopePusher()
|
||||
|
@ -241,12 +267,98 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
for (auto& it : m_identifier_groups) {
|
||||
auto const& identifier_group_name = it.key;
|
||||
auto& identifier_group = it.value;
|
||||
|
||||
if (identifier_group_name == "arguments"sv) {
|
||||
// NOTE: arguments is a special variable that should not be treated as a candidate to become local
|
||||
continue;
|
||||
}
|
||||
|
||||
bool scope_has_declaration = false;
|
||||
MUST(m_node->for_each_var_declared_name([&](auto const& name) {
|
||||
if (m_function_parameters.has_value() && m_forbidden_lexical_names.contains(name))
|
||||
return;
|
||||
if (name == identifier_group_name)
|
||||
scope_has_declaration = true;
|
||||
}));
|
||||
MUST(m_node->for_each_lexically_declared_name([&](auto const& name) {
|
||||
if (name == identifier_group_name)
|
||||
scope_has_declaration = true;
|
||||
}));
|
||||
|
||||
bool function_declaration = false;
|
||||
MUST(m_node->for_each_var_function_declaration_in_reverse_order([&](auto const& declaration) {
|
||||
if (declaration.name() == identifier_group_name)
|
||||
function_declaration = true;
|
||||
}));
|
||||
MUST(m_node->for_each_lexical_function_declaration_in_reverse_order([&](auto const& declaration) {
|
||||
if (declaration.name() == identifier_group_name)
|
||||
function_declaration = true;
|
||||
}));
|
||||
MUST(m_node->for_each_function_hoistable_with_annexB_extension([&](auto const& declaration) {
|
||||
if (declaration.name() == identifier_group_name)
|
||||
function_declaration = true;
|
||||
}));
|
||||
|
||||
if ((m_type == ScopeType::ClassDeclaration || m_type == ScopeType::Catch) && m_bound_names.contains(identifier_group_name)) {
|
||||
// NOTE: Currently class names and catch section parameters are not considered to become local variables
|
||||
// but this might be fixed in the future
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_type == ScopeType::ClassDeclaration || m_type == ScopeType::Catch) {
|
||||
// NOTE: Class declaration and catch scopes do not have own ScopeNode hence can't contain declaration of any variable
|
||||
scope_has_declaration = false;
|
||||
}
|
||||
|
||||
if (scope_has_declaration) {
|
||||
if (function_declaration)
|
||||
continue;
|
||||
|
||||
if (!identifier_group.captured_by_nested_function) {
|
||||
auto function_scope = last_function_scope();
|
||||
if (!function_scope || !m_can_use_local_variables) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto local_variable_index = function_scope->m_node->add_local_variable(identifier_group_name);
|
||||
for (auto& identifier : identifier_group.identifiers)
|
||||
identifier->set_local_variable_index(local_variable_index);
|
||||
}
|
||||
} else {
|
||||
if (m_function_parameters.has_value() || m_type == ScopeType::ClassField || m_type == ScopeType::ClassStaticInit || m_type == ScopeType::With) {
|
||||
// NOTE: Class fields and class static initialization sections implicitly create functions
|
||||
identifier_group.captured_by_nested_function = true;
|
||||
}
|
||||
|
||||
if (m_parent_scope) {
|
||||
if (auto maybe_parent_scope_identifier_group = m_parent_scope->m_identifier_groups.get(identifier_group_name); maybe_parent_scope_identifier_group.has_value()) {
|
||||
maybe_parent_scope_identifier_group.value().identifiers.extend(identifier_group.identifiers);
|
||||
if (identifier_group.captured_by_nested_function)
|
||||
maybe_parent_scope_identifier_group.value().captured_by_nested_function = true;
|
||||
} else {
|
||||
m_parent_scope->m_identifier_groups.set(identifier_group_name, identifier_group);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_parent_scope && !m_function_parameters.has_value()) {
|
||||
m_parent_scope->m_contains_access_to_arguments_object |= m_contains_access_to_arguments_object;
|
||||
m_parent_scope->m_contains_direct_call_to_eval |= m_contains_direct_call_to_eval;
|
||||
m_parent_scope->m_contains_await_expression |= m_contains_await_expression;
|
||||
}
|
||||
|
||||
if (m_parent_scope) {
|
||||
if (m_contains_direct_call_to_eval) {
|
||||
m_parent_scope->m_can_use_local_variables = false;
|
||||
} else {
|
||||
m_can_use_local_variables = m_parent_scope->m_can_use_local_variables && m_can_use_local_variables;
|
||||
}
|
||||
}
|
||||
|
||||
VERIFY(m_parser.m_state.current_scope_pusher == this);
|
||||
m_parser.m_state.current_scope_pusher = m_parent_scope;
|
||||
}
|
||||
|
@ -266,6 +378,18 @@ public:
|
|||
return m_scope_level != ScopeLevel::ScriptTopLevel;
|
||||
}
|
||||
|
||||
void register_identifier(NonnullRefPtr<Identifier> id)
|
||||
{
|
||||
if (auto maybe_identifier_group = m_identifier_groups.get(id->string()); maybe_identifier_group.has_value()) {
|
||||
maybe_identifier_group.value().identifiers.append(id);
|
||||
} else {
|
||||
m_identifier_groups.set(id->string(), IdentifierGroup {
|
||||
.captured_by_nested_function = false,
|
||||
.identifiers = { id },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void throw_identifier_declared(DeprecatedFlyString const& name, NonnullRefPtr<Declaration const> const& declaration)
|
||||
{
|
||||
|
@ -275,6 +399,7 @@ private:
|
|||
Parser& m_parser;
|
||||
ScopeNode* m_node { nullptr };
|
||||
ScopeLevel m_scope_level { ScopeLevel::NotTopLevel };
|
||||
ScopeType m_type;
|
||||
|
||||
ScopePusher* m_parent_scope { nullptr };
|
||||
ScopePusher* m_top_level_scope { nullptr };
|
||||
|
@ -287,11 +412,20 @@ private:
|
|||
HashTable<DeprecatedFlyString> m_forbidden_var_names;
|
||||
Vector<NonnullRefPtr<FunctionDeclaration const>> m_functions_to_hoist;
|
||||
|
||||
HashTable<DeprecatedFlyString> m_bound_names;
|
||||
|
||||
struct IdentifierGroup {
|
||||
bool captured_by_nested_function { false };
|
||||
Vector<NonnullRefPtr<Identifier>> identifiers;
|
||||
};
|
||||
HashMap<DeprecatedFlyString, IdentifierGroup> m_identifier_groups;
|
||||
|
||||
Optional<Vector<FunctionParameter>> m_function_parameters;
|
||||
|
||||
bool m_contains_access_to_arguments_object { false };
|
||||
bool m_contains_direct_call_to_eval { false };
|
||||
bool m_contains_await_expression { false };
|
||||
bool m_can_use_local_variables { true };
|
||||
};
|
||||
|
||||
class OperatorPrecedenceTable {
|
||||
|
@ -860,6 +994,8 @@ RefPtr<FunctionExpression const> Parser::try_parse_arrow_function_expression(boo
|
|||
if (function_body_result.is_null())
|
||||
return nullptr;
|
||||
|
||||
auto local_variables_names = function_body_result->local_variables_names();
|
||||
|
||||
state_rollback_guard.disarm();
|
||||
discard_saved_state();
|
||||
auto body = function_body_result.release_nonnull();
|
||||
|
@ -878,9 +1014,9 @@ RefPtr<FunctionExpression const> Parser::try_parse_arrow_function_expression(boo
|
|||
auto function_end_offset = position().offset - m_state.current_token.trivia().length();
|
||||
auto source_text = DeprecatedString { m_state.lexer.source().substring_view(function_start_offset, function_end_offset - function_start_offset) };
|
||||
return create_ast_node<FunctionExpression>(
|
||||
{ m_source_code, rule_start.position(), position() }, "", move(source_text),
|
||||
{ m_source_code, rule_start.position(), position() }, nullptr, move(source_text),
|
||||
move(body), move(parameters), function_length, function_kind, body->in_strict_mode(),
|
||||
/* might_need_arguments_object */ false, contains_direct_call_to_eval, /* is_arrow_function */ true);
|
||||
/* might_need_arguments_object */ false, contains_direct_call_to_eval, move(local_variables_names), /* is_arrow_function */ true);
|
||||
}
|
||||
|
||||
RefPtr<LabelledStatement const> Parser::try_parse_labelled_statement(AllowLabelledFunction allow_function)
|
||||
|
@ -1071,14 +1207,13 @@ NonnullRefPtr<ClassExpression const> Parser::parse_class_expression(bool expect_
|
|||
RefPtr<FunctionExpression const> constructor;
|
||||
HashTable<DeprecatedFlyString> found_private_names;
|
||||
|
||||
RefPtr<Identifier const> class_name { nullptr };
|
||||
RefPtr<Identifier const> class_name;
|
||||
if (expect_class_name || match_identifier() || match(TokenType::Yield) || match(TokenType::Await)) {
|
||||
class_name = create_ast_node<Identifier const>(
|
||||
{ m_source_code, rule_start.position(), position() },
|
||||
consume_identifier_reference().DeprecatedFlyString_value()
|
||||
);
|
||||
class_name = create_identifier_and_register_in_current_scope({ m_source_code, rule_start.position(), position() }, consume_identifier_reference().DeprecatedFlyString_value());
|
||||
}
|
||||
|
||||
ScopePusher class_declaration_scope = ScopePusher::class_declaration_scope(*this, class_name);
|
||||
|
||||
if (class_name)
|
||||
check_identifier_name_for_assignment_validity(class_name->string(), true);
|
||||
if (m_state.in_class_static_init_block && class_name && class_name->string() == "await"sv)
|
||||
|
@ -1337,7 +1472,8 @@ NonnullRefPtr<ClassExpression const> Parser::parse_class_expression(bool expect_
|
|||
TemporaryChange super_property_access_rollback(m_state.allow_super_property_lookup, true);
|
||||
TemporaryChange field_initializer_rollback(m_state.in_class_field_initializer, true);
|
||||
|
||||
auto class_field_scope = ScopePusher::class_field_scope(*this);
|
||||
auto class_scope_node = create_ast_node<BlockStatement>({ m_source_code, rule_start.position(), position() });
|
||||
auto class_field_scope = ScopePusher::class_field_scope(*this, *class_scope_node);
|
||||
initializer = parse_expression(2);
|
||||
contains_direct_call_to_eval = class_field_scope.contains_direct_call_to_eval();
|
||||
}
|
||||
|
@ -1370,14 +1506,14 @@ NonnullRefPtr<ClassExpression const> Parser::parse_class_expression(bool expect_
|
|||
constructor_body->append(create_ast_node<ReturnStatement>({ m_source_code, rule_start.position(), position() }, move(super_call)));
|
||||
|
||||
constructor = create_ast_node<FunctionExpression>(
|
||||
{ m_source_code, rule_start.position(), position() }, class_name ? class_name->string() : "", "",
|
||||
{ m_source_code, rule_start.position(), position() }, class_name, "",
|
||||
move(constructor_body), Vector { FunctionParameter { move(argument_name), nullptr, true } }, 0, FunctionKind::Normal,
|
||||
/* is_strict_mode */ true, /* might_need_arguments_object */ false, /* contains_direct_call_to_eval */ false);
|
||||
/* is_strict_mode */ true, /* might_need_arguments_object */ false, /* contains_direct_call_to_eval */ false, /* local_variables_names */ Vector<DeprecatedFlyString> {});
|
||||
} else {
|
||||
constructor = create_ast_node<FunctionExpression>(
|
||||
{ m_source_code, rule_start.position(), position() }, class_name ? class_name->string() : "", "",
|
||||
{ m_source_code, rule_start.position(), position() }, class_name, "",
|
||||
move(constructor_body), Vector<FunctionParameter> {}, 0, FunctionKind::Normal,
|
||||
/* is_strict_mode */ true, /* might_need_arguments_object */ false, /* contains_direct_call_to_eval */ false);
|
||||
/* is_strict_mode */ true, /* might_need_arguments_object */ false, /* contains_direct_call_to_eval */ false, /* local_variables_names */ Vector<DeprecatedFlyString> {});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1759,7 +1895,7 @@ NonnullRefPtr<ObjectExpression const> Parser::parse_object_expression()
|
|||
property_key = parse_property_key();
|
||||
} else {
|
||||
property_key = create_ast_node<StringLiteral>({ m_source_code, rule_start.position(), position() }, identifier.value());
|
||||
property_value = create_ast_node<Identifier>({ m_source_code, rule_start.position(), position() }, identifier.DeprecatedFlyString_value());
|
||||
property_value = create_identifier_and_register_in_current_scope({ m_source_code, rule_start.position(), position() }, identifier.DeprecatedFlyString_value());
|
||||
}
|
||||
} else {
|
||||
property_key = parse_property_key();
|
||||
|
@ -2277,6 +2413,7 @@ RefPtr<BindingPattern const> Parser::synthesize_binding_pattern(Expression const
|
|||
Lexer lexer { source, m_state.lexer.filename(), expression.source_range().start.line, expression.source_range().start.column };
|
||||
Parser parser { lexer };
|
||||
|
||||
parser.m_state.current_scope_pusher = m_state.current_scope_pusher;
|
||||
parser.m_state.strict_mode = m_state.strict_mode;
|
||||
parser.m_state.allow_super_property_lookup = m_state.allow_super_property_lookup;
|
||||
parser.m_state.allow_super_constructor_call = m_state.allow_super_constructor_call;
|
||||
|
@ -2355,9 +2492,7 @@ NonnullRefPtr<Identifier const> Parser::parse_identifier()
|
|||
auto token = consume_identifier();
|
||||
if (m_state.in_class_field_initializer && token.value() == "arguments"sv)
|
||||
syntax_error("'arguments' is not allowed in class field initializer");
|
||||
return create_ast_node<Identifier>(
|
||||
{ m_source_code, identifier_start, position() },
|
||||
token.DeprecatedFlyString_value());
|
||||
return create_identifier_and_register_in_current_scope({ m_source_code, identifier_start, position() }, token.DeprecatedFlyString_value());
|
||||
}
|
||||
|
||||
Vector<CallExpression::Argument> Parser::parse_arguments()
|
||||
|
@ -2619,7 +2754,7 @@ NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(u16 parse_options, O
|
|||
function_kind = FunctionKind::Async;
|
||||
else
|
||||
function_kind = FunctionKind::Normal;
|
||||
DeprecatedFlyString name;
|
||||
RefPtr<Identifier const> name;
|
||||
if (parse_options & FunctionNodeParseOptions::CheckForFunctionAndName) {
|
||||
if (function_kind == FunctionKind::Normal && match(TokenType::Async) && !next_token().trivia_contains_line_terminator()) {
|
||||
function_kind = FunctionKind::Async;
|
||||
|
@ -2633,20 +2768,29 @@ NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(u16 parse_options, O
|
|||
parse_options |= FunctionNodeParseOptions::IsGeneratorFunction;
|
||||
}
|
||||
|
||||
if (parse_options & FunctionNodeParseOptions::HasDefaultExportName)
|
||||
name = ExportStatement::local_name_for_default;
|
||||
else if (FunctionNodeType::must_have_name() || match_identifier())
|
||||
name = consume_identifier().DeprecatedFlyString_value();
|
||||
else if (is_function_expression && (match(TokenType::Yield) || match(TokenType::Await)))
|
||||
name = consume().DeprecatedFlyString_value();
|
||||
if (parse_options & FunctionNodeParseOptions::HasDefaultExportName) {
|
||||
name = create_ast_node<Identifier const>(
|
||||
{ m_source_code, rule_start.position(), position() },
|
||||
ExportStatement::local_name_for_default);
|
||||
} else if (FunctionNodeType::must_have_name() || match_identifier()) {
|
||||
name = create_ast_node<Identifier const>(
|
||||
{ m_source_code, rule_start.position(), position() },
|
||||
consume_identifier().DeprecatedFlyString_value());
|
||||
} else if (is_function_expression && (match(TokenType::Yield) || match(TokenType::Await))) {
|
||||
name = create_ast_node<Identifier const>(
|
||||
{ m_source_code, rule_start.position(), position() },
|
||||
consume().DeprecatedFlyString_value());
|
||||
}
|
||||
|
||||
check_identifier_name_for_assignment_validity(name);
|
||||
if (name) {
|
||||
check_identifier_name_for_assignment_validity(name->string());
|
||||
|
||||
if (function_kind == FunctionKind::AsyncGenerator && (name == "await"sv || name == "yield"sv))
|
||||
syntax_error(DeprecatedString::formatted("async generator function is not allowed to be called '{}'", name));
|
||||
if (function_kind == FunctionKind::AsyncGenerator && (name->string() == "await"sv || name->string() == "yield"sv))
|
||||
syntax_error(DeprecatedString::formatted("async generator function is not allowed to be called '{}'", name->string()));
|
||||
|
||||
if (m_state.in_class_static_init_block && name == "await"sv)
|
||||
syntax_error("'await' is a reserved word");
|
||||
if (m_state.in_class_static_init_block && name->string() == "await"sv)
|
||||
syntax_error("'await' is a reserved word");
|
||||
}
|
||||
}
|
||||
TemporaryChange class_static_initializer_rollback(m_state.in_class_static_init_block, false);
|
||||
TemporaryChange generator_change(m_state.in_generator_function_context, function_kind == FunctionKind::Generator || function_kind == FunctionKind::AsyncGenerator);
|
||||
|
@ -2670,12 +2814,13 @@ NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(u16 parse_options, O
|
|||
consume(TokenType::CurlyOpen);
|
||||
bool contains_direct_call_to_eval = false;
|
||||
auto body = parse_function_body(parameters, function_kind, contains_direct_call_to_eval);
|
||||
auto local_variables_names = body->local_variables_names();
|
||||
consume(TokenType::CurlyClose);
|
||||
|
||||
auto has_strict_directive = body->in_strict_mode();
|
||||
|
||||
if (has_strict_directive)
|
||||
check_identifier_name_for_assignment_validity(name, true);
|
||||
if (has_strict_directive && name)
|
||||
check_identifier_name_for_assignment_validity(name->string(), true);
|
||||
|
||||
auto function_start_offset = rule_start.position().offset;
|
||||
auto function_end_offset = position().offset - m_state.current_token.trivia().length();
|
||||
|
@ -2684,7 +2829,8 @@ NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(u16 parse_options, O
|
|||
{ m_source_code, rule_start.position(), position() },
|
||||
name, move(source_text), move(body), move(parameters), function_length,
|
||||
function_kind, has_strict_directive, m_state.function_might_need_arguments_object,
|
||||
contains_direct_call_to_eval);
|
||||
contains_direct_call_to_eval,
|
||||
move(local_variables_names));
|
||||
}
|
||||
|
||||
Vector<FunctionParameter> Parser::parse_formal_parameters(int& function_length, u16 parse_options)
|
||||
|
@ -2842,20 +2988,13 @@ RefPtr<BindingPattern const> Parser::parse_binding_pattern(Parser::AllowDuplicat
|
|||
if (match(TokenType::StringLiteral)) {
|
||||
auto token = consume(TokenType::StringLiteral);
|
||||
auto string_literal = parse_string_literal(token);
|
||||
|
||||
name = create_ast_node<Identifier const>(
|
||||
{ m_source_code, rule_start.position(), position() },
|
||||
string_literal->value());
|
||||
name = create_identifier_and_register_in_current_scope({ m_source_code, rule_start.position(), position() }, string_literal->value());
|
||||
} else if (match(TokenType::BigIntLiteral)) {
|
||||
auto string_value = consume().DeprecatedFlyString_value();
|
||||
VERIFY(string_value.ends_with("n"sv));
|
||||
name = create_ast_node<Identifier const>(
|
||||
{ m_source_code, rule_start.position(), position() },
|
||||
DeprecatedFlyString(string_value.view().substring_view(0, string_value.length() - 1)));
|
||||
name = create_identifier_and_register_in_current_scope({ m_source_code, rule_start.position(), position() }, DeprecatedFlyString(string_value.view().substring_view(0, string_value.length() - 1)));
|
||||
} else {
|
||||
name = create_ast_node<Identifier const>(
|
||||
{ m_source_code, rule_start.position(), position() },
|
||||
consume().DeprecatedFlyString_value());
|
||||
name = create_identifier_and_register_in_current_scope({ m_source_code, rule_start.position(), position() }, consume().DeprecatedFlyString_value());
|
||||
}
|
||||
} else if (match(TokenType::BracketOpen)) {
|
||||
consume();
|
||||
|
@ -2891,10 +3030,7 @@ RefPtr<BindingPattern const> Parser::parse_binding_pattern(Parser::AllowDuplicat
|
|||
return {};
|
||||
alias = binding_pattern.release_nonnull();
|
||||
} else if (match_identifier_name()) {
|
||||
alias = create_ast_node<Identifier const>(
|
||||
{ m_source_code, rule_start.position(), position() },
|
||||
consume().DeprecatedFlyString_value());
|
||||
|
||||
alias = create_identifier_and_register_in_current_scope({ m_source_code, rule_start.position(), position() }, consume().DeprecatedFlyString_value());
|
||||
} else {
|
||||
expected("identifier or binding pattern");
|
||||
return {};
|
||||
|
@ -2930,9 +3066,7 @@ RefPtr<BindingPattern const> Parser::parse_binding_pattern(Parser::AllowDuplicat
|
|||
} else if (match_identifier_name()) {
|
||||
// BindingElement must always have an Empty name field
|
||||
auto identifier_name = consume_identifier().DeprecatedFlyString_value();
|
||||
alias = create_ast_node<Identifier const>(
|
||||
{ m_source_code, rule_start.position(), position() },
|
||||
identifier_name);
|
||||
alias = create_identifier_and_register_in_current_scope({ m_source_code, rule_start.position(), position() }, identifier_name);
|
||||
} else {
|
||||
expected("identifier or binding pattern");
|
||||
return {};
|
||||
|
@ -2996,26 +3130,19 @@ RefPtr<Identifier const> Parser::parse_lexical_binding()
|
|||
auto binding_start = push_start();
|
||||
|
||||
if (match_identifier()) {
|
||||
auto name = consume_identifier().DeprecatedFlyString_value();
|
||||
return create_ast_node<Identifier>(
|
||||
{ m_source_code, binding_start.position(), position() },
|
||||
name);
|
||||
return create_identifier_and_register_in_current_scope({ m_source_code, binding_start.position(), position() }, consume_identifier().DeprecatedFlyString_value());
|
||||
}
|
||||
if (!m_state.in_generator_function_context && match(TokenType::Yield)) {
|
||||
if (m_state.strict_mode)
|
||||
syntax_error("Identifier must not be a reserved word in strict mode ('yield')");
|
||||
|
||||
return create_ast_node<Identifier>(
|
||||
{ m_source_code, binding_start.position(), position() },
|
||||
consume().DeprecatedFlyString_value());
|
||||
return create_identifier_and_register_in_current_scope({ m_source_code, binding_start.position(), position() }, consume().DeprecatedFlyString_value());
|
||||
}
|
||||
if (!m_state.await_expression_is_valid && match(TokenType::Async)) {
|
||||
if (m_program_type == Program::Type::Module)
|
||||
syntax_error("Identifier must not be a reserved word in modules ('async')");
|
||||
|
||||
return create_ast_node<Identifier>(
|
||||
{ m_source_code, binding_start.position(), position() },
|
||||
consume().DeprecatedFlyString_value());
|
||||
return create_identifier_and_register_in_current_scope({ m_source_code, binding_start.position(), position() }, consume().DeprecatedFlyString_value());
|
||||
}
|
||||
|
||||
return {};
|
||||
|
@ -3433,6 +3560,9 @@ NonnullRefPtr<WithStatement const> Parser::parse_with_statement()
|
|||
|
||||
consume(TokenType::ParenClose);
|
||||
|
||||
auto with_scope_node = create_ast_node<BlockStatement>({ m_source_code, rule_start.position(), position() });
|
||||
ScopePusher with_scope = ScopePusher::with_scope(*this, with_scope_node);
|
||||
|
||||
auto body = parse_statement();
|
||||
return create_ast_node<WithStatement>({ m_source_code, rule_start.position(), position() }, move(object), move(body));
|
||||
}
|
||||
|
@ -3573,6 +3703,9 @@ NonnullRefPtr<Statement const> Parser::parse_for_statement()
|
|||
auto rule_start = push_start();
|
||||
auto is_await_loop = IsForAwaitLoop::No;
|
||||
|
||||
auto loop_scope_node = create_ast_node<BlockStatement>({ m_source_code, rule_start.position(), position() });
|
||||
ScopePusher for_loop_scope = ScopePusher::for_loop_scope(*this, *loop_scope_node);
|
||||
|
||||
auto match_of = [&](Token const& token) {
|
||||
return token.type() == TokenType::Identifier && token.original_value() == "of"sv;
|
||||
};
|
||||
|
@ -3638,18 +3771,7 @@ NonnullRefPtr<Statement const> Parser::parse_for_statement()
|
|||
init = move(declaration);
|
||||
} else if (match_variable_declaration()) {
|
||||
auto declaration = parse_variable_declaration(IsForLoopVariableDeclaration::Yes);
|
||||
if (declaration->declaration_kind() == DeclarationKind::Var) {
|
||||
m_state.current_scope_pusher->add_declaration(declaration);
|
||||
} else {
|
||||
// This does not follow the normal declaration structure so we need additional checks.
|
||||
HashTable<DeprecatedFlyString> bound_names;
|
||||
// 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)
|
||||
syntax_error(DeprecatedString::formatted("Identifier '{}' already declared in for loop initializer", name), declaration->source_range().start);
|
||||
}));
|
||||
}
|
||||
|
||||
m_state.current_scope_pusher->add_declaration(declaration);
|
||||
if (match_for_in_of()) {
|
||||
if (declaration->declarations().size() > 1)
|
||||
syntax_error("Multiple declarations not allowed in for..in/of");
|
||||
|
@ -3697,7 +3819,7 @@ NonnullRefPtr<Statement const> Parser::parse_for_statement()
|
|||
|
||||
TemporaryChange break_change(m_state.in_break_context, true);
|
||||
TemporaryChange continue_change(m_state.in_continue_context, true);
|
||||
ScopePusher for_loop_scope = ScopePusher::for_loop_scope(*this, init);
|
||||
|
||||
auto body = parse_statement();
|
||||
|
||||
return create_ast_node<ForStatement>({ m_source_code, rule_start.position(), position() }, move(init), move(test), move(update), move(body));
|
||||
|
@ -3753,7 +3875,7 @@ NonnullRefPtr<Statement const> Parser::parse_for_in_of_statement(NonnullRefPtr<A
|
|||
|
||||
TemporaryChange break_change(m_state.in_break_context, true);
|
||||
TemporaryChange continue_change(m_state.in_continue_context, true);
|
||||
ScopePusher for_loop_scope = ScopePusher::for_loop_scope(*this, lhs);
|
||||
|
||||
auto body = parse_statement();
|
||||
if (is_in)
|
||||
return create_ast_node<ForInStatement>({ m_source_code, rule_start.position(), position() }, move(for_declaration), move(rhs), move(body));
|
||||
|
@ -4862,4 +4984,12 @@ Parser::ForbiddenTokens Parser::ForbiddenTokens::forbid(std::initializer_list<To
|
|||
|
||||
template NonnullRefPtr<FunctionExpression> Parser::parse_function_node(u16, Optional<Position> const&);
|
||||
template NonnullRefPtr<FunctionDeclaration> Parser::parse_function_node(u16, Optional<Position> const&);
|
||||
|
||||
NonnullRefPtr<Identifier const> Parser::create_identifier_and_register_in_current_scope(SourceRange range, DeprecatedFlyString string)
|
||||
{
|
||||
auto id = create_ast_node<Identifier const>(range, string);
|
||||
m_state.current_scope_pusher->register_identifier(const_cast<Identifier&>(*id));
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -342,6 +342,8 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
NonnullRefPtr<Identifier const> create_identifier_and_register_in_current_scope(SourceRange range, DeprecatedFlyString string);
|
||||
|
||||
NonnullRefPtr<SourceCode const> m_source_code;
|
||||
Vector<Position> m_rule_starts;
|
||||
ParserState m_state;
|
||||
|
|
Loading…
Add table
Reference in a new issue