From f7c15d00c9edcd34f555969f9a0a8bb4eccd044b Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 15 Mar 2020 15:01:10 +0100 Subject: LibJS: Add basic prototype support Object will now traverse up the prototype chain when doing a get(). When a function is called on an object, that object will now also be the "this" value inside the function. This stuff is probably not very correct, but we will improve things as we go! :^) --- Libraries/LibJS/AST.cpp | 13 ++++++++++++- Libraries/LibJS/AST.h | 4 ++++ Libraries/LibJS/Interpreter.h | 10 ++++++++++ Libraries/LibJS/Object.cpp | 11 ++++++++++- Libraries/LibJS/Object.h | 6 ++++++ 5 files changed, 42 insertions(+), 2 deletions(-) (limited to 'Libraries') 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 }; }; } -- cgit v1.2.3