mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-26 17:40:27 +00:00
LibJS: Add missing cyclic prototype check to Object.setPrototypeOf()
This commit is contained in:
parent
ebb40e7d7b
commit
4e555fae22
Notes:
sideshowbarker
2024-07-18 12:39:53 +09:00
2 changed files with 30 additions and 4 deletions
|
@ -17,6 +17,7 @@
|
|||
#include <LibJS/Runtime/NativeFunction.h>
|
||||
#include <LibJS/Runtime/NativeProperty.h>
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
#include <LibJS/Runtime/ProxyObject.h>
|
||||
#include <LibJS/Runtime/Shape.h>
|
||||
#include <LibJS/Runtime/StringObject.h>
|
||||
#include <LibJS/Runtime/TemporaryClearException.h>
|
||||
|
@ -110,17 +111,32 @@ const Object* Object::prototype() const
|
|||
return shape().prototype();
|
||||
}
|
||||
|
||||
// 10.1.2.1 OrdinarySetPrototypeOf, https://tc39.es/ecma262/#sec-ordinarysetprototypeof
|
||||
bool Object::set_prototype(Object* new_prototype)
|
||||
{
|
||||
if (prototype() == new_prototype)
|
||||
return true;
|
||||
if (!m_is_extensible)
|
||||
return false;
|
||||
if (shape().is_unique()) {
|
||||
shape().set_prototype_without_transition(new_prototype);
|
||||
return true;
|
||||
auto* prototype = new_prototype;
|
||||
while (prototype) {
|
||||
if (prototype == this)
|
||||
return false;
|
||||
// NOTE: This is a best-effort implementation of the following step:
|
||||
// "If p.[[GetPrototypeOf]] is not the ordinary object internal method defined in 10.1.1,
|
||||
// set done to true."
|
||||
// We don't have a good way of detecting whether certain virtual Object methods have been
|
||||
// overridden by a given object, but as ProxyObject is the only one doing that, this check
|
||||
// does the trick.
|
||||
if (is<ProxyObject>(prototype))
|
||||
break;
|
||||
prototype = prototype->prototype();
|
||||
}
|
||||
m_shape = m_shape->create_prototype_transition(new_prototype);
|
||||
auto& shape = this->shape();
|
||||
if (shape.is_unique())
|
||||
shape.set_prototype_without_transition(new_prototype);
|
||||
else
|
||||
m_shape = shape.create_prototype_transition(new_prototype);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,4 +40,14 @@ describe("errors", () => {
|
|||
|
||||
expect(Object.setPrototypeOf(o, p)).toBe(o);
|
||||
});
|
||||
|
||||
test("cyclic prototype chain", () => {
|
||||
let o = {};
|
||||
let p = {};
|
||||
Object.setPrototypeOf(o, p);
|
||||
|
||||
expect(() => {
|
||||
Object.setPrototypeOf(p, o);
|
||||
}).toThrowWithMessage(TypeError, "Object's [[SetPrototypeOf]] method returned false");
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue