0
0
Fork 0
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:
Romain Marcadier 2022-01-24 11:09:05 +01:00 committed by GitHub
parent 4b5514711a
commit 729c5b323f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 84 additions and 0 deletions

View file

@ -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));

View file

@ -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.

View file

@ -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());
}
}