mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 17:10:23 +00:00
LibJS: Add Object.{isExtensible,preventExtensions}()
This commit is contained in:
parent
daa3f59a55
commit
d5ae73a63b
Notes:
sideshowbarker
2024-07-19 05:54:04 +09:00
Author: https://github.com/mattco98 Commit: https://github.com/SerenityOS/serenity/commit/d5ae73a63b0 Pull-request: https://github.com/SerenityOS/serenity/pull/2474
6 changed files with 118 additions and 0 deletions
|
@ -89,6 +89,12 @@ bool Object::has_prototype(const Object* prototype) const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Object::prevent_extensions()
|
||||
{
|
||||
m_is_extensible = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Value Object::get_own_property(const Object& this_object, PropertyName property_name) const
|
||||
{
|
||||
Value value_here;
|
||||
|
@ -308,6 +314,13 @@ bool Object::put_own_property(Object& this_object, const FlyString& property_nam
|
|||
{
|
||||
ASSERT(!(mode == PutOwnPropertyMode::Put && value.is_accessor()));
|
||||
|
||||
if (!is_extensible()) {
|
||||
dbg() << "Disallow define_property of non-extensible object";
|
||||
if (throw_exceptions && interpreter().in_strict_mode())
|
||||
interpreter().throw_exception<TypeError>(String::format("Cannot define property %s on non-extensible object", property_name.characters()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value.is_accessor()) {
|
||||
auto& accessor = value.as_accessor();
|
||||
if (accessor.getter())
|
||||
|
@ -375,6 +388,13 @@ bool Object::put_own_property_by_index(Object& this_object, u32 property_index,
|
|||
{
|
||||
ASSERT(!(mode == PutOwnPropertyMode::Put && value.is_accessor()));
|
||||
|
||||
if (!is_extensible()) {
|
||||
dbg() << "Disallow define_property of non-extensible object";
|
||||
if (throw_exceptions && interpreter().in_strict_mode())
|
||||
interpreter().throw_exception<TypeError>(String::format("Cannot define property %d on non-extensible object", property_index));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value.is_accessor()) {
|
||||
auto& accessor = value.as_accessor();
|
||||
if (accessor.getter())
|
||||
|
|
|
@ -98,6 +98,9 @@ public:
|
|||
void set_prototype(Object*);
|
||||
bool has_prototype(const Object* prototype) const;
|
||||
|
||||
bool is_extensible() const { return m_is_extensible; }
|
||||
bool prevent_extensions();
|
||||
|
||||
virtual Value value_of() const { return Value(const_cast<Object*>(this)); }
|
||||
virtual Value to_primitive(Value::PreferredType preferred_type = Value::PreferredType::Default) const;
|
||||
virtual Value to_string() const;
|
||||
|
@ -122,6 +125,7 @@ private:
|
|||
void set_shape(Shape&);
|
||||
void ensure_shape_is_unique();
|
||||
|
||||
bool m_is_extensible { true };
|
||||
Shape* m_shape { nullptr };
|
||||
Vector<Value> m_storage;
|
||||
IndexedProperties m_indexed_properties;
|
||||
|
|
|
@ -48,6 +48,8 @@ ObjectConstructor::ObjectConstructor()
|
|||
define_native_function("getOwnPropertyNames", get_own_property_names, 1, attr);
|
||||
define_native_function("getPrototypeOf", get_prototype_of, 1, attr);
|
||||
define_native_function("setPrototypeOf", set_prototype_of, 2, attr);
|
||||
define_native_function("isExtensible", is_extensible, 1, attr);
|
||||
define_native_function("preventExtensions", prevent_extensions, 1, attr);
|
||||
define_native_function("keys", keys, 1, attr);
|
||||
define_native_function("values", values, 1, attr);
|
||||
define_native_function("entries", entries, 1, attr);
|
||||
|
@ -104,6 +106,26 @@ Value ObjectConstructor::set_prototype_of(Interpreter& interpreter)
|
|||
return {};
|
||||
}
|
||||
|
||||
Value ObjectConstructor::is_extensible(Interpreter& interpreter)
|
||||
{
|
||||
auto argument = interpreter.argument(0);
|
||||
if (!argument.is_object())
|
||||
return Value(false);
|
||||
return Value(argument.as_object().is_extensible());
|
||||
}
|
||||
|
||||
Value ObjectConstructor::prevent_extensions(Interpreter& interpreter)
|
||||
{
|
||||
auto argument = interpreter.argument(0);
|
||||
if (!argument.is_object())
|
||||
return argument;
|
||||
if (!argument.as_object().prevent_extensions()) {
|
||||
interpreter.throw_exception<TypeError>("Proxy preventExtensions handler returned false");
|
||||
return {};
|
||||
}
|
||||
return argument;
|
||||
}
|
||||
|
||||
Value ObjectConstructor::get_own_property_descriptor(Interpreter& interpreter)
|
||||
{
|
||||
auto* object = interpreter.argument(0).to_object(interpreter);
|
||||
|
|
|
@ -48,6 +48,8 @@ private:
|
|||
static Value get_own_property_names(Interpreter&);
|
||||
static Value get_prototype_of(Interpreter&);
|
||||
static Value set_prototype_of(Interpreter&);
|
||||
static Value is_extensible(Interpreter&);
|
||||
static Value prevent_extensions(Interpreter&);
|
||||
static Value keys(Interpreter&);
|
||||
static Value values(Interpreter&);
|
||||
static Value entries(Interpreter&);
|
||||
|
|
22
Libraries/LibJS/Tests/Object.isExtensible.js
Normal file
22
Libraries/LibJS/Tests/Object.isExtensible.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
load("test-common.js");
|
||||
|
||||
try {
|
||||
assert(Object.isExtensible() === false);
|
||||
assert(Object.isExtensible(undefined) === false);
|
||||
assert(Object.isExtensible(null) === false);
|
||||
assert(Object.isExtensible(true) === false);
|
||||
assert(Object.isExtensible(6) === false);
|
||||
assert(Object.isExtensible("test") === false);
|
||||
|
||||
let s = Symbol();
|
||||
assert(Object.isExtensible(s) === false);
|
||||
|
||||
let o = { foo: "foo" };
|
||||
assert(Object.isExtensible(o) === true);
|
||||
Object.preventExtensions(o);
|
||||
assert(Object.isExtensible(o) === false);
|
||||
|
||||
console.log("PASS");
|
||||
} catch (e) {
|
||||
console.log("FAIL: " + e);
|
||||
}
|
48
Libraries/LibJS/Tests/Object.preventExtensions.js
Normal file
48
Libraries/LibJS/Tests/Object.preventExtensions.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
load("test-common.js");
|
||||
|
||||
try {
|
||||
assert(Object.preventExtensions() === undefined);
|
||||
assert(Object.preventExtensions(undefined) === undefined);
|
||||
assert(Object.preventExtensions(null) === null);
|
||||
assert(Object.preventExtensions(true) === true);
|
||||
assert(Object.preventExtensions(6) === 6);
|
||||
assert(Object.preventExtensions("test") === "test");
|
||||
|
||||
let s = Symbol();
|
||||
assert(Object.preventExtensions(s) === s);
|
||||
|
||||
let o = { foo: "foo" };
|
||||
assert(o.foo === "foo");
|
||||
o.bar = "bar";
|
||||
assert(o.bar === "bar");
|
||||
|
||||
assert(Object.preventExtensions(o) === o);
|
||||
assert(o.foo === "foo");
|
||||
assert(o.bar === "bar");
|
||||
|
||||
o.baz = "baz";
|
||||
assert(o.baz === undefined);
|
||||
|
||||
Object.defineProperty(o, "baz", { value: "baz" });
|
||||
assert(o.baz === undefined);
|
||||
|
||||
assertThrowsError(() => {
|
||||
"use strict";
|
||||
o.baz = "baz";
|
||||
}, {
|
||||
error: TypeError,
|
||||
message: "Cannot define property baz on non-extensible object",
|
||||
});
|
||||
|
||||
assertThrowsError(() => {
|
||||
"use strict";
|
||||
Object.defineProperty(o, "baz", { value: "baz" });
|
||||
}, {
|
||||
error: TypeError,
|
||||
message: "Cannot define property baz on non-extensible object",
|
||||
});
|
||||
|
||||
console.log("PASS");
|
||||
} catch (e) {
|
||||
console.log("FAIL: " + e);
|
||||
}
|
Loading…
Reference in a new issue