Explorar el Código

LibJS: Disallow changing the prototype of non-extensible objects

Object::set_prototype() now returns a boolean indicating success.
Setting the prototype to an identical object is always considered
successful, even if the object is non-extensible.
Linus Groh hace 5 años
padre
commit
b958e4f573

+ 6 - 3
Libraries/LibJS/Runtime/Object.cpp

@@ -69,15 +69,18 @@ const Object* Object::prototype() const
     return shape().prototype();
 }
 
-void Object::set_prototype(Object* new_prototype)
+bool Object::set_prototype(Object* new_prototype)
 {
     if (prototype() == new_prototype)
-        return;
+        return true;
+    if (!m_is_extensible)
+        return false;
     if (shape().is_unique()) {
         shape().set_prototype_without_transition(new_prototype);
-        return;
+        return true;
     }
     m_shape = m_shape->create_prototype_transition(new_prototype);
+    return true;
 }
 
 bool Object::has_prototype(const Object* prototype) const

+ 1 - 1
Libraries/LibJS/Runtime/Object.h

@@ -95,7 +95,7 @@ public:
 
     Object* prototype();
     const Object* prototype() const;
-    void set_prototype(Object*);
+    bool set_prototype(Object* prototype);
     bool has_prototype(const Object* prototype) const;
 
     bool is_extensible() const { return m_is_extensible; }

+ 4 - 1
Libraries/LibJS/Runtime/ObjectConstructor.cpp

@@ -112,7 +112,10 @@ Value ObjectConstructor::set_prototype_of(Interpreter& interpreter)
         interpreter.throw_exception<TypeError>("Prototype must be null or object");
         return {};
     }
-    object->set_prototype(prototype);
+    if (!object->set_prototype(prototype)) {
+        interpreter.throw_exception<TypeError>("Can't set prototype of non-extensible object");
+        return {};
+    }
     return object;
 }
 

+ 11 - 1
Libraries/LibJS/Tests/Object.setPrototypeOf.js

@@ -11,7 +11,17 @@ try {
     });
 
     o = {};
-    assert(Object.setPrototypeOf(o, {}) === o);
+    p = {};
+    assert(Object.setPrototypeOf(o, p) === o);
+
+    Object.preventExtensions(o);
+    assertThrowsError(() => {
+        Object.setPrototypeOf(o, {});
+    }, {
+        error: TypeError,
+        message: "Can't set prototype of non-extensible object"
+    });
+    assert(Object.setPrototypeOf(o, p) === o);
 
     console.log("PASS");
 } catch (e) {