diff options
-rw-r--r-- | Libraries/LibJS/AST.cpp | 13 | ||||
-rw-r--r-- | Libraries/LibJS/AST.h | 4 | ||||
-rw-r--r-- | Libraries/LibJS/Interpreter.h | 10 | ||||
-rw-r--r-- | Libraries/LibJS/Object.cpp | 11 | ||||
-rw-r--r-- | Libraries/LibJS/Object.h | 6 |
5 files changed, 42 insertions, 2 deletions
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<const MemberExpression&>(*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<Expression> 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<Argument>, 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<ScopeFrame> m_scope_stack; + Vector<Value> 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<Value(Interp void Object::visit_children(Cell::Visitor& visitor) { Cell::visit_children(visitor); + if (m_prototype) + visitor.visit(m_prototype); for (auto& it : m_properties) visitor.visit(it.value); } diff --git a/Libraries/LibJS/Object.h b/Libraries/LibJS/Object.h index 30cc74bf6e..e3b8f1e4f9 100644 --- a/Libraries/LibJS/Object.h +++ b/Libraries/LibJS/Object.h @@ -45,12 +45,18 @@ public: virtual bool is_function() const { return false; } virtual bool is_native_function() const { return false; } + virtual bool is_string_object() const { return false; } virtual const char* class_name() const override { return "Object"; } virtual void visit_children(Cell::Visitor&) override; + Object* prototype() { return m_prototype; } + const Object* prototype() const { return m_prototype; } + void set_prototype(Object* prototype) { m_prototype = prototype; } + private: HashMap<String, Value> m_properties; + Object* m_prototype { nullptr }; }; } |