Reimplement [[SetPrototypeOf]](V) and Object.setPrototypeOf(O, proto) according to 6.0 (#377)

* Don't throw type error with Reflect.setPrototypeOf
* Pass more test262 tests

Signed-off-by: Boram Bae <boram21.bae@samsung.com>
This commit is contained in:
Boram Bae 2019-08-19 16:52:09 +09:00 committed by Patrick Kim
commit 8e4f170f3f
5 changed files with 66 additions and 38 deletions

View file

@ -429,39 +429,49 @@ Object* Object::createFunctionPrototypeObject(ExecutionState& state, FunctionObj
bool Object::setPrototype(ExecutionState& state, const Value& proto)
{
if (!proto.isObject() && !proto.isNull()) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "can't set prototype of this object");
return false;
}
// https://www.ecma-international.org/ecma-262/6.0/#sec-ordinary-object-internal-methods-and-internal-slots-setprototypeof-v
// [[SetPrototypeOf]] (V)
Value object = this;
Value current = this->getPrototype(state);
// 1. Assert: Either Type(V) is Object or Type(V) is Null.
ASSERT(proto.isObject() || proto.isNull());
if (proto == current) {
// 3. Let current be the value of the [[Prototype]] internal slot of O.
// 4. If SameValue(V, current), return true.
if (proto == this->getPrototype(state)) {
return true;
}
if (UNLIKELY(!isOrdinary() || !isExtensible(state))) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "can't set prototype of this object");
// 2. Let extensible be the value of the [[Extensible]] internal slot of O.
// 5. If extensible is false, return false.
if (!isExtensible(state)) {
return false;
}
Value nextProto = proto;
while (nextProto && nextProto.isObject()) {
if (nextProto.asObject() == this) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "cyclic __proto__");
// 6. Let p be V.
Value p = proto;
// 7. Let done be false.
bool done = false;
// 8. Repeat while done is false,
while (done == false) {
if (p.isNull()) { // If p is null, let done be true.
done = true;
} else if (p.isObject() && p.asObject() == this) { // Else, if SameValue(p, O) is true, return false.
return false;
} else { // Else,
// i. If the [[GetPrototypeOf]] internal method of p is not the ordinary object internal method defined in 9.1.1, let done be true.
if (UNLIKELY(!p.isObject() && p.asObject()->isOrdinary())) {
done = true;
} else { // ii. Else, let p be the value of ps [[Prototype]] internal slot.
p = p.asObject()->getPrototype(state);
}
}
if (UNLIKELY(!nextProto.asObject()->isOrdinary())) {
break;
}
nextProto = nextProto.asObject()->getPrototype(state);
}
//9. Set the value of the [[Prototype]] internal slot of O to V.
Object* o = nullptr;
if (LIKELY(proto.isObject())) {
proto.asObject()->markAsPrototypeObject(state);
o = proto.asObject();
o->markAsPrototypeObject(state);
}
if (rareData()) {
@ -470,6 +480,7 @@ bool Object::setPrototype(ExecutionState& state, const Value& proto)
m_prototype = o;
}
// 10. Return true.
return true;
}