1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 14:27:35 +00:00

LibJS: Fix that functions in module did not look for var declarations

This commit is contained in:
davidot 2022-11-15 01:39:07 +01:00 committed by Linus Groh
parent 385c2f2eb8
commit 67865306d3
3 changed files with 52 additions and 17 deletions

View file

@ -22,29 +22,46 @@ namespace JS {
class ScopePusher { class ScopePusher {
// NOTE: We really only need ModuleTopLevel and NotModuleTopLevel as the only
// difference seems to be in https://tc39.es/ecma262/#sec-static-semantics-varscopeddeclarations
// where ModuleItemList only does the VarScopedDeclaration and not the
// TopLevelVarScopedDeclarations.
enum class ScopeLevel {
NotTopLevel,
ScriptTopLevel,
ModuleTopLevel,
FunctionTopLevel,
StaticInitTopLevel
};
private: private:
ScopePusher(Parser& parser, ScopeNode* node, bool is_top_level) ScopePusher(Parser& parser, ScopeNode* node, ScopeLevel scope_level)
: m_parser(parser) : m_parser(parser)
, m_is_top_level(is_top_level) , m_scope_level(scope_level)
{ {
m_parent_scope = exchange(m_parser.m_state.current_scope_pusher, this); m_parent_scope = exchange(m_parser.m_state.current_scope_pusher, this);
VERIFY(node || (m_parent_scope && !is_top_level)); VERIFY(node || (m_parent_scope && scope_level == ScopeLevel::NotTopLevel));
if (!node) if (!node)
m_node = m_parent_scope->m_node; m_node = m_parent_scope->m_node;
else else
m_node = node; m_node = node;
VERIFY(m_node); VERIFY(m_node);
if (!is_top_level) if (!is_top_level())
m_top_level_scope = m_parent_scope->m_top_level_scope; m_top_level_scope = m_parent_scope->m_top_level_scope;
else else
m_top_level_scope = this; m_top_level_scope = this;
} }
bool is_top_level()
{
return m_scope_level != ScopeLevel::NotTopLevel;
}
public: public:
static ScopePusher function_scope(Parser& parser, FunctionBody& function_body, Vector<FunctionDeclaration::Parameter> const& parameters) static ScopePusher function_scope(Parser& parser, FunctionBody& function_body, Vector<FunctionDeclaration::Parameter> const& parameters)
{ {
ScopePusher scope_pusher(parser, &function_body, true); ScopePusher scope_pusher(parser, &function_body, ScopeLevel::FunctionTopLevel);
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(
@ -62,17 +79,17 @@ public:
static ScopePusher program_scope(Parser& parser, Program& program) static ScopePusher program_scope(Parser& parser, Program& program)
{ {
return ScopePusher(parser, &program, true); return ScopePusher(parser, &program, program.type() == Program::Type::Script ? ScopeLevel::ScriptTopLevel : ScopeLevel::ModuleTopLevel);
} }
static ScopePusher block_scope(Parser& parser, ScopeNode& node) static ScopePusher block_scope(Parser& parser, ScopeNode& node)
{ {
return ScopePusher(parser, &node, false); return ScopePusher(parser, &node, ScopeLevel::NotTopLevel);
} }
static ScopePusher for_loop_scope(Parser& parser, RefPtr<ASTNode> const& init) static ScopePusher for_loop_scope(Parser& parser, RefPtr<ASTNode> const& init)
{ {
ScopePusher scope_pusher(parser, nullptr, false); ScopePusher scope_pusher(parser, nullptr, ScopeLevel::NotTopLevel);
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) {
@ -87,7 +104,7 @@ public:
static ScopePusher catch_scope(Parser& parser, RefPtr<BindingPattern> const& pattern, FlyString const& parameter) static ScopePusher catch_scope(Parser& parser, RefPtr<BindingPattern> const& pattern, FlyString const& parameter)
{ {
ScopePusher scope_pusher(parser, nullptr, false); ScopePusher scope_pusher(parser, nullptr, ScopeLevel::NotTopLevel);
if (pattern) { if (pattern) {
pattern->for_each_bound_name([&](auto const& name) { pattern->for_each_bound_name([&](auto const& name) {
scope_pusher.m_forbidden_var_names.set(name); scope_pusher.m_forbidden_var_names.set(name);
@ -100,12 +117,12 @@ public:
static ScopePusher static_init_block_scope(Parser& parser, ScopeNode& node) static ScopePusher static_init_block_scope(Parser& parser, ScopeNode& node)
{ {
return ScopePusher(parser, &node, true); return ScopePusher(parser, &node, ScopeLevel::StaticInitTopLevel);
} }
static ScopePusher class_field_scope(Parser& parser) static ScopePusher class_field_scope(Parser& parser)
{ {
return ScopePusher(parser, nullptr, false); return ScopePusher(parser, nullptr, ScopeLevel::NotTopLevel);
} }
void add_declaration(NonnullRefPtr<Declaration> declaration) void add_declaration(NonnullRefPtr<Declaration> declaration)
@ -130,20 +147,21 @@ public:
throw_identifier_declared(name, declaration); throw_identifier_declared(name, declaration);
pusher->m_var_names.set(name); pusher->m_var_names.set(name);
if (pusher->m_is_top_level) if (pusher->is_top_level())
break; break;
VERIFY(pusher->m_parent_scope != nullptr); VERIFY(pusher->m_parent_scope != nullptr);
pusher = pusher->m_parent_scope; pusher = pusher->m_parent_scope;
} }
VERIFY(pusher->m_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_is_top_level && m_parser.m_program_type == Program::Type::Script) { if (m_scope_level != ScopeLevel::NotTopLevel && m_scope_level != ScopeLevel::ModuleTopLevel) {
// Only non-top levels and Module don't var declare the top functions
declaration->for_each_bound_name([&](auto const& name) { declaration->for_each_bound_name([&](auto const& name) {
m_var_names.set(name); m_var_names.set(name);
}); });
@ -202,7 +220,7 @@ public:
~ScopePusher() ~ScopePusher()
{ {
VERIFY(m_is_top_level || m_parent_scope); VERIFY(is_top_level() || m_parent_scope);
if (!m_contains_access_to_arguments_object) { if (!m_contains_access_to_arguments_object) {
for (auto& it : m_identifier_and_argument_index_associations) { for (auto& it : m_identifier_and_argument_index_associations) {
@ -217,7 +235,7 @@ public:
auto const& function_declaration = m_functions_to_hoist[i]; auto const& function_declaration = m_functions_to_hoist[i];
if (m_lexical_names.contains(function_declaration.name()) || m_forbidden_var_names.contains(function_declaration.name())) if (m_lexical_names.contains(function_declaration.name()) || m_forbidden_var_names.contains(function_declaration.name()))
continue; continue;
if (m_is_top_level) if (is_top_level())
m_node->add_hoisted_function(move(m_functions_to_hoist[i])); m_node->add_hoisted_function(move(m_functions_to_hoist[i]));
else else
m_parent_scope->m_functions_to_hoist.append(move(m_functions_to_hoist[i])); m_parent_scope->m_functions_to_hoist.append(move(m_functions_to_hoist[i]));
@ -255,7 +273,7 @@ private:
Parser& m_parser; Parser& m_parser;
ScopeNode* m_node { nullptr }; ScopeNode* m_node { nullptr };
bool m_is_top_level { false }; ScopeLevel m_scope_level { ScopeLevel::NotTopLevel };
ScopePusher* m_parent_scope { nullptr }; ScopePusher* m_parent_scope { nullptr };
ScopePusher* m_top_level_scope { nullptr }; ScopePusher* m_top_level_scope { nullptr };

View file

@ -239,3 +239,9 @@ describe("failing modules cascade", () => {
); );
}); });
}); });
describe("scoping in modules", () => {
test("functions within functions", () => {
expectModulePassed("./function-in-function.mjs");
});
});

View file

@ -0,0 +1,11 @@
function foo() {
function bar() {}
bar.baz = "value on bar";
return bar;
}
foo.bippity = "boppity";
const fooResult = foo();
export const passed = fooResult.baz === "value on bar" && foo.bippity === "boppity";