mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-05 15:40:31 +00:00
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! :^)
This commit is contained in:
parent
e5ebdb9bca
commit
82ca7ae1f8
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue