diff options
author | Hendi <hendi48@users.noreply.github.com> | 2021-07-05 21:45:34 +0200 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-07-06 00:15:37 +0100 |
commit | 3411d50737725b382ae526e91a8bbd60656c3323 (patch) | |
tree | de589cc7b9cc9b85d102ae4ed45dd5974a2b2bf9 /Userland/Libraries | |
parent | c194afd17c501195a7598e4f07c8f040d0d66221 (diff) | |
download | serenity-3411d50737725b382ae526e91a8bbd60656c3323.zip |
LibJS: Don't hoist functions under certain circumstances
When a lexical declaration with the same name as a function exists,
the function is not hoisted (annex B).
Diffstat (limited to 'Userland/Libraries')
-rw-r--r-- | Userland/Libraries/LibJS/AST.cpp | 4 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/AST.h | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Parser.cpp | 39 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Parser.h | 8 |
4 files changed, 45 insertions, 8 deletions
diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index 23b16fb21b..db0485caf0 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -2377,9 +2377,9 @@ void ScopeNode::add_functions(NonnullRefPtrVector<FunctionDeclaration> functions m_functions.extend(move(functions)); } -void ScopeNode::add_hoisted_functions(NonnullRefPtrVector<FunctionDeclaration> hoisted_functions) +void ScopeNode::add_hoisted_function(NonnullRefPtr<FunctionDeclaration> hoisted_function) { - m_hoisted_functions.extend(move(hoisted_functions)); + m_hoisted_functions.append(hoisted_function); } } diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index f6bc2639de..7b94fe38af 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -145,7 +145,7 @@ public: void add_variables(NonnullRefPtrVector<VariableDeclaration>); void add_functions(NonnullRefPtrVector<FunctionDeclaration>); - void add_hoisted_functions(NonnullRefPtrVector<FunctionDeclaration>); + void add_hoisted_function(NonnullRefPtr<FunctionDeclaration>); NonnullRefPtrVector<VariableDeclaration> const& variables() const { return m_variables; } NonnullRefPtrVector<FunctionDeclaration> const& functions() const { return m_functions; } NonnullRefPtrVector<FunctionDeclaration> const& hoisted_functions() const { return m_hoisted_functions; } diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index bbfceebeec..21dae61d20 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -64,7 +64,24 @@ public: scope_node->add_variables(m_parser.m_state.let_scopes.last()); scope_node->add_functions(m_parser.m_state.current_scope->function_declarations); - scope_node->add_hoisted_functions(m_parser.m_state.current_scope->hoisted_function_declarations); + + for (auto& hoistable_function : m_parser.m_state.current_scope->hoisted_function_declarations) { + if (is_hoistable(hoistable_function)) { + scope_node->add_hoisted_function(hoistable_function.declaration); + } + } + } + + static bool is_hoistable(Parser::Scope::HoistableDeclaration& declaration) + { + auto& name = declaration.declaration->name(); + // See if we find any conflicting lexical declaration on the way up + for (RefPtr<Parser::Scope> scope = declaration.scope; !scope.is_null(); scope = scope->parent) { + if (scope->lexical_declarations.contains(name)) { + return false; + } + } + return true; } Parser& m_parser; @@ -304,7 +321,8 @@ NonnullRefPtr<Declaration> Parser::parse_declaration() case TokenType::Function: { auto declaration = parse_function_node<FunctionDeclaration>(); m_state.current_scope->function_declarations.append(declaration); - m_state.current_scope->get_current_function_scope()->hoisted_function_declarations.append(declaration); + auto hoisting_target = m_state.current_scope->get_current_function_scope(); + hoisting_target->hoisted_function_declarations.append({ declaration, *m_state.current_scope }); return declaration; } case TokenType::Let: @@ -1760,10 +1778,23 @@ NonnullRefPtr<VariableDeclaration> Parser::parse_variable_declaration(bool for_l consume_or_insert_semicolon(); auto declaration = create_ast_node<VariableDeclaration>({ m_state.current_token.filename(), rule_start.position(), position() }, declaration_kind, move(declarations)); - if (declaration_kind == DeclarationKind::Var) + if (declaration_kind == DeclarationKind::Var) { m_state.var_scopes.last().append(declaration); - else + } else { m_state.let_scopes.last().append(declaration); + + for (auto& declarator : declaration->declarations()) { + declarator.target().visit( + [&](const NonnullRefPtr<Identifier>& id) { + m_state.current_scope->lexical_declarations.set(id->string()); + }, + [&](const NonnullRefPtr<BindingPattern>& binding) { + binding->for_each_bound_name([&](const auto& name) { + m_state.current_scope->lexical_declarations.set(name); + }); + }); + } + } return declaration; } diff --git a/Userland/Libraries/LibJS/Parser.h b/Userland/Libraries/LibJS/Parser.h index 0e231a3dff..cef0813585 100644 --- a/Userland/Libraries/LibJS/Parser.h +++ b/Userland/Libraries/LibJS/Parser.h @@ -201,12 +201,18 @@ private: Function, Block, }; + struct HoistableDeclaration { + NonnullRefPtr<FunctionDeclaration> declaration; + NonnullRefPtr<Scope> scope; // where it is actually declared + }; Type type; RefPtr<Scope> parent; NonnullRefPtrVector<FunctionDeclaration> function_declarations; - NonnullRefPtrVector<FunctionDeclaration> hoisted_function_declarations; + Vector<HoistableDeclaration> hoisted_function_declarations; + + HashTable<FlyString> lexical_declarations; explicit Scope(Type, RefPtr<Scope>); RefPtr<Scope> get_current_function_scope(); |