mirror of
https://github.com/denoland/rusty_v8.git
synced 2025-01-11 08:34:01 -05:00
Add support for v8::Object.set_integrity_level (#874)
This allows making objects frozen or sealed from rust code. This is useful to control mutations that are allowed to happen in JS on the given object.
This commit is contained in:
parent
4b5514711a
commit
729c5b323f
3 changed files with 84 additions and 0 deletions
|
@ -1175,6 +1175,18 @@ const v8::Value* v8__Object__GetInternalField(const v8::Object& self,
|
|||
return local_to_ptr(ptr_to_local(&self)->GetInternalField(index));
|
||||
}
|
||||
|
||||
static_assert(static_cast<int>(v8::IntegrityLevel::kFrozen) == 0,
|
||||
"v8::IntegrityLevel::kFrozen is not 0");
|
||||
static_assert(static_cast<int>(v8::IntegrityLevel::kSealed) == 1,
|
||||
"v8::IntegrityLevel::kSealed is not 1");
|
||||
|
||||
MaybeBool v8__Object__SetIntegrityLevel(const v8::Object& self,
|
||||
const v8::Context& context,
|
||||
v8::IntegrityLevel level) {
|
||||
return maybe_to_maybe_bool(
|
||||
ptr_to_local(&self)->SetIntegrityLevel(ptr_to_local(&context), level));
|
||||
}
|
||||
|
||||
void v8__Object__SetInternalField(const v8::Object& self, int index,
|
||||
const v8::Value& value) {
|
||||
ptr_to_local(&self)->SetInternalField(index, ptr_to_local(&value));
|
||||
|
|
|
@ -114,6 +114,11 @@ extern "C" {
|
|||
this: *const Object,
|
||||
index: int,
|
||||
) -> *const Value;
|
||||
fn v8__Object__SetIntegrityLevel(
|
||||
this: *const Object,
|
||||
context: *const Context,
|
||||
level: IntegrityLevel,
|
||||
) -> MaybeBool;
|
||||
fn v8__Object__SetInternalField(
|
||||
this: *const Object,
|
||||
index: int,
|
||||
|
@ -487,6 +492,18 @@ impl Object {
|
|||
None
|
||||
}
|
||||
|
||||
/// Sets the integrity level of the object.
|
||||
pub fn set_integrity_level(
|
||||
&self,
|
||||
scope: &mut HandleScope,
|
||||
level: IntegrityLevel,
|
||||
) -> Option<bool> {
|
||||
unsafe {
|
||||
v8__Object__SetIntegrityLevel(self, &*scope.get_current_context(), level)
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Sets the value in an internal field. Returns false when the index
|
||||
/// is out of bounds, true otherwise.
|
||||
pub fn set_internal_field(&self, index: usize, value: Local<Value>) -> bool {
|
||||
|
@ -571,6 +588,20 @@ impl Object {
|
|||
}
|
||||
}
|
||||
|
||||
/// Object integrity levels can be used to restrict what can be done to an
|
||||
/// object's properties.
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub enum IntegrityLevel {
|
||||
/// Frozen objects are like Sealed objects, except all existing properties are
|
||||
/// also made non-writable.
|
||||
Frozen,
|
||||
/// Sealed objects prevent addition of any new property on the object, makes
|
||||
/// all existing properties non-configurable, meaning they cannot be deleted,
|
||||
/// have their enumerability, configurability, or writability changed.
|
||||
Sealed,
|
||||
}
|
||||
|
||||
impl Array {
|
||||
/// Creates a JavaScript array with the given length. If the length
|
||||
/// is negative the returned array will have length 0.
|
||||
|
|
|
@ -1530,6 +1530,7 @@ fn object() {
|
|||
let null: v8::Local<v8::Value> = v8::null(scope).into();
|
||||
let n1: v8::Local<v8::Name> = v8::String::new(scope, "a").unwrap().into();
|
||||
let n2: v8::Local<v8::Name> = v8::String::new(scope, "b").unwrap().into();
|
||||
let p = v8::String::new(scope, "p").unwrap().into();
|
||||
let v1: v8::Local<v8::Value> = v8::Number::new(scope, 1.0).into();
|
||||
let v2: v8::Local<v8::Value> = v8::Number::new(scope, 2.0).into();
|
||||
let object = v8::Object::with_prototype_and_properties(
|
||||
|
@ -1553,6 +1554,46 @@ fn object() {
|
|||
assert!(!object.has(scope, n_unused).unwrap());
|
||||
assert!(object.delete(scope, n1.into()).unwrap());
|
||||
assert!(!object.has(scope, n1.into()).unwrap());
|
||||
|
||||
let global = context.global(scope);
|
||||
let object_string = v8::String::new(scope, "o").unwrap().into();
|
||||
global.set(scope, object_string, object.into());
|
||||
|
||||
assert!(eval(scope, "Object.isExtensible(o)").unwrap().is_true());
|
||||
assert!(eval(scope, "Object.isSealed(o)").unwrap().is_false());
|
||||
assert!(eval(scope, "Object.isFrozen(o)").unwrap().is_false());
|
||||
|
||||
assert!(object
|
||||
.set_integrity_level(scope, v8::IntegrityLevel::Sealed)
|
||||
.unwrap());
|
||||
|
||||
assert!(eval(scope, "Object.isExtensible(o)").unwrap().is_false());
|
||||
assert!(eval(scope, "Object.isSealed(o)").unwrap().is_true());
|
||||
assert!(eval(scope, "Object.isFrozen(o)").unwrap().is_false());
|
||||
// Creating new properties is not allowed anymore
|
||||
eval(scope, "o.p = true").unwrap();
|
||||
assert!(!object.has(scope, p).unwrap());
|
||||
// Deleting properties is not allowed anymore
|
||||
eval(scope, "delete o.b").unwrap();
|
||||
assert!(object.has(scope, n2.into()).unwrap());
|
||||
// But we can still write new values.
|
||||
assert!(eval(scope, "o.b = true; o.b").unwrap().is_true());
|
||||
|
||||
assert!(object
|
||||
.set_integrity_level(scope, v8::IntegrityLevel::Frozen)
|
||||
.unwrap());
|
||||
|
||||
assert!(eval(scope, "Object.isExtensible(o)").unwrap().is_false());
|
||||
assert!(eval(scope, "Object.isSealed(o)").unwrap().is_true());
|
||||
assert!(eval(scope, "Object.isFrozen(o)").unwrap().is_true());
|
||||
// Creating new properties is not allowed anymore
|
||||
eval(scope, "o.p = true").unwrap();
|
||||
assert!(!object.has(scope, p).unwrap());
|
||||
// Deleting properties is not allowed anymore
|
||||
eval(scope, "delete o.b").unwrap();
|
||||
assert!(object.has(scope, n2.into()).unwrap());
|
||||
// And we can also not write new values
|
||||
assert!(eval(scope, "o.b = false; o.b").unwrap().is_true());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue