diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index b60824bcd06..3cb34f15bc5 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -56,7 +56,7 @@ Value CallExpression::execute(Interpreter& interpreter) const auto* callee_object = callee.as_object(); ASSERT(callee_object->is_function()); auto& function = static_cast(*callee_object); - return interpreter.run(function.body()); + return interpreter.run(function.body(), ScopeType::Function); } Value ReturnStatement::execute(Interpreter& interpreter) const @@ -379,17 +379,30 @@ void AssignmentExpression::dump(int indent) const Value VariableDeclaration::execute(Interpreter& interpreter) const { - interpreter.declare_variable(name().string()); + interpreter.declare_variable(name().string(), m_declaration_type); if (m_initializer) { auto initalizer_result = m_initializer->execute(interpreter); interpreter.set_variable(name().string(), initalizer_result); } + return js_undefined(); } void VariableDeclaration::dump(int indent) const { + const char* op_string = nullptr; + switch (m_declaration_type) { + case DeclarationType::Let: + op_string = "Let"; + break; + case DeclarationType::Var: + op_string = "Var"; + break; + } + ASTNode::dump(indent); + print_indent(indent + 1); + printf("%s\n", op_string); m_name->dump(indent + 1); if (m_initializer) m_initializer->dump(indent + 1); diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h index 30b18272544..e61285d9cd6 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -332,10 +332,16 @@ private: NonnullOwnPtr m_rhs; }; +enum class DeclarationType { + Var, + Let, +}; + class VariableDeclaration : public ASTNode { public: - VariableDeclaration(NonnullOwnPtr name, OwnPtr initializer) - : m_name(move(name)) + VariableDeclaration(NonnullOwnPtr name, OwnPtr initializer, DeclarationType declaration_type) + : m_declaration_type(declaration_type) + , m_name(move(name)) , m_initializer(move(initializer)) { } @@ -348,6 +354,7 @@ public: private: virtual const char* class_name() const override { return "VariableDeclaration"; } + DeclarationType m_declaration_type; NonnullOwnPtr m_name; OwnPtr m_initializer; }; diff --git a/Libraries/LibJS/Forward.h b/Libraries/LibJS/Forward.h index 64f5beeb9af..0af451f63e8 100644 --- a/Libraries/LibJS/Forward.h +++ b/Libraries/LibJS/Forward.h @@ -37,5 +37,6 @@ class Object; class PrimitiveString; class ScopeNode; class Value; +enum class DeclarationType; } diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index 701782a1212..3983998c554 100644 --- a/Libraries/LibJS/Interpreter.cpp +++ b/Libraries/LibJS/Interpreter.cpp @@ -42,9 +42,9 @@ Interpreter::~Interpreter() { } -Value Interpreter::run(const ScopeNode& scope_node) +Value Interpreter::run(const ScopeNode& scope_node, ScopeType scope_type) { - enter_scope(scope_node); + enter_scope(scope_node, scope_type); Value last_value = js_undefined(); for (auto& node : scope_node.children()) { @@ -55,9 +55,9 @@ Value Interpreter::run(const ScopeNode& scope_node) return last_value; } -void Interpreter::enter_scope(const ScopeNode& scope_node) +void Interpreter::enter_scope(const ScopeNode& scope_node, ScopeType scope_type) { - m_scope_stack.append({ scope_node, {} }); + m_scope_stack.append({ scope_type, scope_node, {} }); } void Interpreter::exit_scope(const ScopeNode& scope_node) @@ -71,9 +71,24 @@ void Interpreter::do_return() dbg() << "FIXME: Implement Interpreter::do_return()"; } -void Interpreter::declare_variable(String name) +void Interpreter::declare_variable(String name, DeclarationType declaration_type) { - m_scope_stack.last().variables.set(move(name), js_undefined()); + switch (declaration_type) { + case DeclarationType::Var: + for (ssize_t i = m_scope_stack.size() - 1; i >= 0; --i) { + auto& scope = m_scope_stack.at(i); + if (scope.type == ScopeType::Function) { + scope.variables.set(move(name), js_undefined()); + return; + } + } + + global_object().put(move(name), js_undefined()); + break; + case DeclarationType::Let: + m_scope_stack.last().variables.set(move(name), js_undefined()); + break; + } } void Interpreter::set_variable(String name, Value value) diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index 0c229c182b7..7f3f4f76d70 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -33,7 +33,13 @@ namespace JS { +enum class ScopeType { + Function, + Block, +}; + struct ScopeFrame { + ScopeType type; const ScopeNode& scope_node; HashMap variables; }; @@ -43,7 +49,7 @@ public: Interpreter(); ~Interpreter(); - Value run(const ScopeNode&); + Value run(const ScopeNode&, ScopeType = ScopeType::Block); Object& global_object() { return *m_global_object; } const Object& global_object() const { return *m_global_object; } @@ -54,12 +60,12 @@ public: Value get_variable(const String& name); void set_variable(String name, Value); - void declare_variable(String name); + void declare_variable(String name, DeclarationType); void collect_roots(Badge, HashTable&); private: - void enter_scope(const ScopeNode&); + void enter_scope(const ScopeNode&, ScopeType); void exit_scope(const ScopeNode&); Heap m_heap; diff --git a/Userland/js.cpp b/Userland/js.cpp index 50648637d18..6b7c567967b 100644 --- a/Userland/js.cpp +++ b/Userland/js.cpp @@ -92,10 +92,12 @@ void build_program(JS::Program& program) auto block = make(); block->append( make("a"), - make(JS::Value(5))); + make(JS::Value(5)), + JS::DeclarationType::Var); block->append( make("b"), - make(JS::Value(7))); + make(JS::Value(7)), + JS::DeclarationType::Var); block->append( make( @@ -120,13 +122,38 @@ void build_program(JS::Program& program) auto block = make(); block->append( make("x"), - make()); + make(), + JS::DeclarationType::Var); block->append("$gc"); program.append("foo", move(block)); program.append("foo"); } #elif PROGRAM == 4 +void build_program(JS::Program& program) +{ + // function foo() { + // function bar() { + // var y = 6; + // } + // + // bar() + // return y; + // } + // foo(); //I should return `undefined` because y is bound to the inner-most enclosing function, i.e the nested one (bar()), therefore, it's undefined in the scope of foo() + + auto block_bar = make(); + block_bar->append(make("y"), make(JS::Value(6)), JS::DeclarationType::Var); + + auto block_foo = make(); + block_foo->append("bar", move(block_bar)); + block_foo->append("bar"); + block_foo->append(make("y")); + + program.append("foo", move(block_foo)); + program.append("foo"); +} +#elif PROGRAM == 5 void build_program(JS::Program& program, JS::Heap& heap) { // "hello friends".length