diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index c5e670fa7a..9a5ca84aaf 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -63,7 +63,18 @@ Value CallExpression::execute(Interpreter& interpreter) const for (size_t i = 0; i < m_arguments.size(); ++i) argument_values.append(m_arguments[i].execute(interpreter)); - return function->call(interpreter, move(argument_values)); + Value this_value = js_undefined(); + if (m_callee->is_member_expression()) + this_value = static_cast(*m_callee).object().execute(interpreter).to_object(interpreter.heap()); + + if (!this_value.is_undefined()) + interpreter.push_this_value(this_value); + + auto result = function->call(interpreter, move(argument_values)); + + if (!this_value.is_undefined()) + interpreter.pop_this_value(); + return result; } Value ReturnStatement::execute(Interpreter& interpreter) const diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h index c5ded7710a..43a2508769 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -142,6 +142,7 @@ private: class Expression : public ASTNode { public: + virtual bool is_member_expression() const { return false; } }; class ErrorExpression final : public Expression { @@ -521,7 +522,10 @@ public: virtual Value execute(Interpreter&) const override; virtual void dump(int indent) const override; + const Expression& object() const { return *m_object; } + private: + virtual bool is_member_expression() const override { return true; } virtual const char* class_name() const override { return "MemberExpression"; } NonnullOwnPtr m_object; diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index 61b584c068..79940cac3a 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -79,10 +79,20 @@ public: void enter_scope(const ScopeNode&, Vector, ScopeType); void exit_scope(const ScopeNode&); + void push_this_value(Value value) { m_this_stack.append(move(value)); } + void pop_this_value() { m_this_stack.take_last(); } + Value this_value() const + { + if (m_this_stack.is_empty()) + return m_global_object; + return m_this_stack.last(); + } + private: Heap m_heap; Vector m_scope_stack; + Vector m_this_stack; Object* m_global_object { nullptr }; }; diff --git a/Libraries/LibJS/Object.cpp b/Libraries/LibJS/Object.cpp index ae1d43397a..8fdd3721b2 100644 --- a/Libraries/LibJS/Object.cpp +++ b/Libraries/LibJS/Object.cpp @@ -42,7 +42,14 @@ Object::~Object() Value Object::get(String property_name) const { - return m_properties.get(property_name).value_or(js_undefined()); + const Object* object = this; + while (object) { + auto value = object->m_properties.get(property_name); + if (value.has_value()) + return value.value(); + object = object->prototype(); + } + return js_undefined(); } void Object::put(String property_name, Value value) @@ -58,6 +65,8 @@ void Object::put_native_function(String property_name, AK::Function m_properties; + Object* m_prototype { nullptr }; }; }