summaryrefslogtreecommitdiff
path: root/Libraries
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-03-28 19:48:12 +0100
committerAndreas Kling <kling@serenityos.org>2020-03-28 19:48:12 +0100
commit82ca7ae1f806d03efbba49c4c1c8404b7863f558 (patch)
treee1bb3818cb62b91007570a461568a32ce2187d43 /Libraries
parente5ebdb9bca3588b2fb01891127771b03dae09ad9 (diff)
downloadserenity-82ca7ae1f806d03efbba49c4c1c8404b7863f558.zip
LibJS: Oops, "instanceof" was backwards!
Fix the "instanceof" operator to check if the constructor's prototype property occurs anywhere in the prototype chain of the instance object. This patch also adds Object.setPrototypeOf() to make it possible to create a test for this bug. Thanks to DexesTTP for pointing this out! :^)
Diffstat (limited to 'Libraries')
-rw-r--r--Libraries/LibJS/Runtime/Object.cpp9
-rw-r--r--Libraries/LibJS/Runtime/Object.h1
-rw-r--r--Libraries/LibJS/Runtime/ObjectConstructor.cpp14
-rw-r--r--Libraries/LibJS/Runtime/Value.cpp15
-rw-r--r--Libraries/LibJS/Tests/instanceof-basic.js31
5 files changed, 53 insertions, 17 deletions
diff --git a/Libraries/LibJS/Runtime/Object.cpp b/Libraries/LibJS/Runtime/Object.cpp
index 3be6803712..1c4e70f5ea 100644
--- a/Libraries/LibJS/Runtime/Object.cpp
+++ b/Libraries/LibJS/Runtime/Object.cpp
@@ -44,6 +44,15 @@ Object::~Object()
{
}
+bool Object::has_prototype(const Object* prototype) const
+{
+ for (auto* object = m_prototype; object; object = object->prototype()) {
+ if (object == prototype)
+ return true;
+ }
+ return false;
+}
+
Optional<Value> Object::get_own_property(const Object& this_object, const FlyString& property_name) const
{
auto value_here = m_properties.get(property_name);
diff --git a/Libraries/LibJS/Runtime/Object.h b/Libraries/LibJS/Runtime/Object.h
index 041ea089ba..aa2d887cbe 100644
--- a/Libraries/LibJS/Runtime/Object.h
+++ b/Libraries/LibJS/Runtime/Object.h
@@ -62,6 +62,7 @@ public:
Object* prototype() { return m_prototype; }
const Object* prototype() const { return m_prototype; }
void set_prototype(Object* prototype) { m_prototype = prototype; }
+ bool has_prototype(const Object* prototype) const;
bool has_own_property(const FlyString& property_name) const;
enum class PreferredType {
diff --git a/Libraries/LibJS/Runtime/ObjectConstructor.cpp b/Libraries/LibJS/Runtime/ObjectConstructor.cpp
index 998cb4b512..11a36da59c 100644
--- a/Libraries/LibJS/Runtime/ObjectConstructor.cpp
+++ b/Libraries/LibJS/Runtime/ObjectConstructor.cpp
@@ -45,6 +45,20 @@ ObjectConstructor::ObjectConstructor()
return {};
return object.as_object()->prototype();
});
+
+ put_native_function("setPrototypeOf", [this](Object*, const Vector<Value>& arguments) -> Value {
+ if (arguments.size() < 2)
+ return {};
+ auto object = arguments[0].to_object(heap());
+ if (interpreter().exception())
+ return {};
+ if (!object.is_object())
+ return {};
+ if (!arguments[1].is_object())
+ return {};
+ const_cast<Object*>(object.as_object())->set_prototype(const_cast<Object*>(arguments[1].as_object()));
+ return {};
+ });
}
ObjectConstructor::~ObjectConstructor()
diff --git a/Libraries/LibJS/Runtime/Value.cpp b/Libraries/LibJS/Runtime/Value.cpp
index c36d358213..fd5671aa10 100644
--- a/Libraries/LibJS/Runtime/Value.cpp
+++ b/Libraries/LibJS/Runtime/Value.cpp
@@ -259,20 +259,11 @@ 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)
+ auto constructor_prototype_property = rhs.as_object()->get("prototype");
+ if (!constructor_prototype_property.has_value() || !constructor_prototype_property.value().is_object())
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);
+ return Value(lhs.as_object()->has_prototype(constructor_prototype_property.value().as_object()));
}
const LogStream& operator<<(const LogStream& stream, const Value& value)
diff --git a/Libraries/LibJS/Tests/instanceof-basic.js b/Libraries/LibJS/Tests/instanceof-basic.js
index 468da2f7fd..5fa9805aa6 100644
--- a/Libraries/LibJS/Tests/instanceof-basic.js
+++ b/Libraries/LibJS/Tests/instanceof-basic.js
@@ -1,7 +1,28 @@
-function Foo() {
- this.x = 123;
-}
+function assert(x) { if (!x) throw 1; }
+
+try {
+ function Foo() {
+ this.x = 123;
+ }
+
+ var foo = new Foo();
+ assert(foo instanceof Foo);
+
+ function Base() {
+ this.is_base = true;
+ }
+
+ function Derived() {
+ this.is_derived = true;
+ }
+
+ Object.setPrototypeOf(Derived.prototype, Base.prototype);
+
+ var d = new Derived();
+ assert(d instanceof Derived);
+ assert(d instanceof Base);
-var foo = new Foo();
-if (foo instanceof Foo)
console.log("PASS");
+} catch(e) {
+ console.log("FAIL: " + e);
+}