Browse Source

LibJS: Implement the "instanceof" operator

This operator walks the prototype chain of the RHS value and looks for
a "prototype" property with the same value as the prototype of the LHS.

This is pretty cool. :^)
Andreas Kling 5 years ago
parent
commit
a3d92b1210

+ 5 - 0
Libraries/LibJS/AST.cpp

@@ -241,6 +241,8 @@ Value BinaryExpression::execute(Interpreter& interpreter) const
         return left_shift(lhs_result, rhs_result);
     case BinaryOp::RightShift:
         return right_shift(lhs_result, rhs_result);
+    case BinaryOp::InstanceOf:
+        return instance_of(lhs_result, rhs_result);
     }
 
     ASSERT_NOT_REACHED();
@@ -368,6 +370,9 @@ void BinaryExpression::dump(int indent) const
     case BinaryOp::RightShift:
         op_string = ">>";
         break;
+    case BinaryOp::InstanceOf:
+        op_string = "instanceof";
+        break;
     }
 
     print_indent(indent);

+ 1 - 0
Libraries/LibJS/AST.h

@@ -302,6 +302,7 @@ enum class BinaryOp {
     BitwiseXor,
     LeftShift,
     RightShift,
+    InstanceOf,
 };
 
 class BinaryExpression : public Expression {

+ 5 - 1
Libraries/LibJS/Parser.cpp

@@ -393,6 +393,9 @@ NonnullRefPtr<Expression> Parser::parse_secondary_expression(NonnullRefPtr<Expre
     case TokenType::ExclamationMarkEquals:
         consume();
         return create_ast_node<BinaryExpression>(BinaryOp::AbstractInequals, move(lhs), parse_expression(min_precedence, associativity));
+    case TokenType::Instanceof:
+        consume();
+        return create_ast_node<BinaryExpression>(BinaryOp::InstanceOf, move(lhs), parse_expression(min_precedence, associativity));
     case TokenType::ParenOpen:
         return parse_call_expression(move(lhs));
     case TokenType::Equals:
@@ -722,7 +725,8 @@ bool Parser::match_secondary_expression() const
         || type == TokenType::Period
         || type == TokenType::BracketOpen
         || type == TokenType::PlusPlus
-        || type == TokenType::MinusMinus;
+        || type == TokenType::MinusMinus
+        || type == TokenType::Instanceof;
 }
 
 bool Parser::match_statement() const

+ 22 - 0
Libraries/LibJS/Runtime/Value.cpp

@@ -24,6 +24,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <AK/FlyString.h>
 #include <AK/String.h>
 #include <LibJS/Heap/Heap.h>
 #include <LibJS/Runtime/Object.h>
@@ -253,6 +254,27 @@ Value eq(Value lhs, Value rhs)
     return Value(false);
 }
 
+Value instance_of(Value lhs, Value rhs)
+{
+    if (!lhs.is_object() || !rhs.is_object())
+        return Value(false);
+
+    auto* instance_prototype = lhs.as_object()->prototype();
+
+    if (!instance_prototype)
+        return Value(false);
+
+    for (auto* constructor_object = rhs.as_object(); constructor_object; constructor_object = constructor_object->prototype()) {
+        auto prototype_property = constructor_object->get_own_property(*constructor_object, "prototype");
+        if (!prototype_property.has_value())
+            continue;
+        if (prototype_property.value().is_object() && prototype_property.value().as_object() == instance_prototype)
+            return Value(true);
+    }
+
+    return Value(false);
+}
+
 const LogStream& operator<<(const LogStream& stream, const Value& value)
 {
     return stream << value.to_string();

+ 1 - 0
Libraries/LibJS/Runtime/Value.h

@@ -189,6 +189,7 @@ Value mul(Value lhs, Value rhs);
 Value div(Value lhs, Value rhs);
 Value eq(Value lhs, Value rhs);
 Value typed_eq(Value lhs, Value rhs);
+Value instance_of(Value lhs, Value rhs);
 
 const LogStream& operator<<(const LogStream&, const Value&);
 

+ 7 - 0
Libraries/LibJS/Tests/instanceof-basic.js

@@ -0,0 +1,7 @@
+function Foo() {
+    this.x = 123;
+}
+
+var foo = new Foo();
+if (foo instanceof Foo)
+    console.log("PASS");