瀏覽代碼

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! :^)
Andreas Kling 5 年之前
父節點
當前提交
f7c15d00c9
共有 5 個文件被更改,包括 42 次插入2 次删除
  1. 12 1
      Libraries/LibJS/AST.cpp
  2. 4 0
      Libraries/LibJS/AST.h
  3. 10 0
      Libraries/LibJS/Interpreter.h
  4. 10 1
      Libraries/LibJS/Object.cpp
  5. 6 0
      Libraries/LibJS/Object.h

+ 12 - 1
Libraries/LibJS/AST.cpp

@@ -63,7 +63,18 @@ Value CallExpression::execute(Interpreter& interpreter) const
     for (size_t i = 0; i < m_arguments.size(); ++i)
     for (size_t i = 0; i < m_arguments.size(); ++i)
         argument_values.append(m_arguments[i].execute(interpreter));
         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
 Value ReturnStatement::execute(Interpreter& interpreter) const

+ 4 - 0
Libraries/LibJS/AST.h

@@ -142,6 +142,7 @@ private:
 
 
 class Expression : public ASTNode {
 class Expression : public ASTNode {
 public:
 public:
+    virtual bool is_member_expression() const { return false; }
 };
 };
 
 
 class ErrorExpression final : public Expression {
 class ErrorExpression final : public Expression {
@@ -521,7 +522,10 @@ public:
     virtual Value execute(Interpreter&) const override;
     virtual Value execute(Interpreter&) const override;
     virtual void dump(int indent) const override;
     virtual void dump(int indent) const override;
 
 
+    const Expression& object() const { return *m_object; }
+
 private:
 private:
+    virtual bool is_member_expression() const override { return true; }
     virtual const char* class_name() const override { return "MemberExpression"; }
     virtual const char* class_name() const override { return "MemberExpression"; }
 
 
     NonnullOwnPtr<Expression> m_object;
     NonnullOwnPtr<Expression> m_object;

+ 10 - 0
Libraries/LibJS/Interpreter.h

@@ -79,10 +79,20 @@ public:
     void enter_scope(const ScopeNode&, Vector<Argument>, ScopeType);
     void enter_scope(const ScopeNode&, Vector<Argument>, ScopeType);
     void exit_scope(const ScopeNode&);
     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:
 private:
     Heap m_heap;
     Heap m_heap;
 
 
     Vector<ScopeFrame> m_scope_stack;
     Vector<ScopeFrame> m_scope_stack;
+    Vector<Value> m_this_stack;
 
 
     Object* m_global_object { nullptr };
     Object* m_global_object { nullptr };
 };
 };

+ 10 - 1
Libraries/LibJS/Object.cpp

@@ -42,7 +42,14 @@ Object::~Object()
 
 
 Value Object::get(String property_name) const
 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)
 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)
 void Object::visit_children(Cell::Visitor& visitor)
 {
 {
     Cell::visit_children(visitor);
     Cell::visit_children(visitor);
+    if (m_prototype)
+        visitor.visit(m_prototype);
     for (auto& it : m_properties)
     for (auto& it : m_properties)
         visitor.visit(it.value);
         visitor.visit(it.value);
 }
 }

+ 6 - 0
Libraries/LibJS/Object.h

@@ -45,12 +45,18 @@ public:
 
 
     virtual bool is_function() const { return false; }
     virtual bool is_function() const { return false; }
     virtual bool is_native_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 const char* class_name() const override { return "Object"; }
     virtual void visit_children(Cell::Visitor&) override;
     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:
 private:
     HashMap<String, Value> m_properties;
     HashMap<String, Value> m_properties;
+    Object* m_prototype { nullptr };
 };
 };
 
 
 }
 }