summaryrefslogtreecommitdiff
path: root/Libraries/LibJS/Runtime
diff options
context:
space:
mode:
authorMatthew Olsson <matthewcolsson@gmail.com>2020-07-11 15:27:28 -0700
committerAndreas Kling <kling@serenityos.org>2020-07-14 20:15:19 +0200
commitdd49ec17a2a3600134722de4f24c995174959f30 (patch)
tree91de0a77e923f80f77c6f6c31575cfc6b649bd52 /Libraries/LibJS/Runtime
parent7bf4b1c9f065b50c07b4d1d59b483032f5802617 (diff)
downloadserenity-dd49ec17a2a3600134722de4f24c995174959f30.zip
LibJS: Implement spec-complient instance_of operation
Diffstat (limited to 'Libraries/LibJS/Runtime')
-rw-r--r--Libraries/LibJS/Runtime/ErrorTypes.h2
-rw-r--r--Libraries/LibJS/Runtime/Value.cpp53
-rw-r--r--Libraries/LibJS/Runtime/Value.h1
3 files changed, 50 insertions, 6 deletions
diff --git a/Libraries/LibJS/Runtime/ErrorTypes.h b/Libraries/LibJS/Runtime/ErrorTypes.h
index 48a63e2b91..7be359176b 100644
--- a/Libraries/LibJS/Runtime/ErrorTypes.h
+++ b/Libraries/LibJS/Runtime/ErrorTypes.h
@@ -42,7 +42,7 @@
M(DescChangeNonConfigurable, "Cannot change attributes of non-configurable property '%s'") \
M(FunctionArgsNotObject, "Argument array must be an object") \
M(InOperatorWithObject, "'in' operator must be used on an object") \
- M(InstanceOfOperatorBadPrototype, "Prototype property of %s is not an object") \
+ M(InstanceOfOperatorBadPrototype, "'prototype' property of %s is not an object") \
M(InvalidAssignToConst, "Invalid assignment to const variable") \
M(InvalidLeftHandAssignment, "Invalid left-hand side in assignment") \
M(IsNotA, "%s is not a %s") \
diff --git a/Libraries/LibJS/Runtime/Value.cpp b/Libraries/LibJS/Runtime/Value.cpp
index c1b67bcb6d..a9fc4fb488 100644
--- a/Libraries/LibJS/Runtime/Value.cpp
+++ b/Libraries/LibJS/Runtime/Value.cpp
@@ -38,6 +38,7 @@
#include <LibJS/Runtime/BigInt.h>
#include <LibJS/Runtime/BigIntObject.h>
#include <LibJS/Runtime/BooleanObject.h>
+#include <LibJS/Runtime/BoundFunction.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/Function.h>
#include <LibJS/Runtime/NumberObject.h>
@@ -685,14 +686,56 @@ Value in(Interpreter& interpreter, Value lhs, Value rhs)
Value instance_of(Interpreter& interpreter, Value lhs, Value rhs)
{
- if (!lhs.is_object() || !rhs.is_object())
+ if (!rhs.is_object())
+ return interpreter.throw_exception<TypeError>(ErrorType::NotAnObject, rhs.to_string_without_side_effects().characters());
+
+ auto has_instance_method = rhs.as_object().get(interpreter.well_known_symbol_has_instance());
+ if (!has_instance_method.is_empty()) {
+ if (!has_instance_method.is_function())
+ return interpreter.throw_exception<TypeError>(ErrorType::NotAFunction, has_instance_method.to_string_without_side_effects().characters());
+
+ MarkedValueList arguments(interpreter.heap());
+ arguments.append(lhs);
+ return Value(interpreter.call(has_instance_method.as_function(), rhs, move(arguments)).to_boolean());
+ }
+
+ if (!rhs.is_function())
+ return interpreter.throw_exception<TypeError>(ErrorType::NotAFunction, rhs.to_string_without_side_effects().characters());
+
+ return ordinary_has_instance(interpreter, lhs, rhs);
+}
+
+Value ordinary_has_instance(Interpreter& interpreter, Value lhs, Value rhs)
+{
+ if (!rhs.is_function())
+ return Value(false);
+ auto& rhs_function = rhs.as_function();
+
+ if (rhs_function.is_bound_function()) {
+ auto& bound_target = static_cast<BoundFunction&>(rhs_function);
+ return instance_of(interpreter, lhs, Value(&bound_target.target_function()));
+ }
+
+ if (!lhs.is_object())
return Value(false);
- auto constructor_prototype_property = rhs.as_object().get("prototype");
+
+ Object* lhs_object = &lhs.as_object();
+ auto rhs_prototype = rhs_function.get("prototype");
if (interpreter.exception())
return {};
- if (!constructor_prototype_property.is_object())
- return Value(false);
- return Value(lhs.as_object().has_prototype(&constructor_prototype_property.as_object()));
+
+ if (!rhs_prototype.is_object())
+ return interpreter.throw_exception<TypeError>(ErrorType::InstanceOfOperatorBadPrototype, rhs_prototype.to_string_without_side_effects().characters());
+
+ while (true) {
+ lhs_object = lhs_object->prototype();
+ if (interpreter.exception())
+ return {};
+ if (!lhs_object)
+ return Value(false);
+ if (same_value(interpreter, rhs_prototype, lhs_object))
+ return Value(true);
+ }
}
const LogStream& operator<<(const LogStream& stream, const Value& value)
diff --git a/Libraries/LibJS/Runtime/Value.h b/Libraries/LibJS/Runtime/Value.h
index 0092232b72..b3a800ecd2 100644
--- a/Libraries/LibJS/Runtime/Value.h
+++ b/Libraries/LibJS/Runtime/Value.h
@@ -323,6 +323,7 @@ Value mod(Interpreter&, Value lhs, Value rhs);
Value exp(Interpreter&, Value lhs, Value rhs);
Value in(Interpreter&, Value lhs, Value rhs);
Value instance_of(Interpreter&, Value lhs, Value rhs);
+Value ordinary_has_instance(Interpreter& interpreter, Value lhs, Value rhs);
bool abstract_eq(Interpreter&, Value lhs, Value rhs);
bool strict_eq(Interpreter&, Value lhs, Value rhs);