From 43b798a39da4972b56267e04dbbce631ac929469 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Thu, 25 May 2023 15:04:50 +0200 Subject: [PATCH] Add more Object methods (#1240) Specifically Object::get_own_property_descriptor and Object::get_property_attributes --- src/binding.cc | 15 ++++++++ src/object.rs | 52 ++++++++++++++++++++++++++ src/property_attribute.rs | 5 +++ tests/test_api.rs | 77 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 149 insertions(+) diff --git a/src/binding.cc b/src/binding.cc index 8702be71..eba421e2 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -1426,6 +1426,21 @@ MaybeBool v8__Object__HasPrivate(const v8::Object& self, ptr_to_local(&context), ptr_to_local(&key))); } +void v8__Object__GetPropertyAttributes( + const v8::Object& self, const v8::Context& context, + const v8::Value& key, v8::Maybe* out) { + *out = ptr_to_local(&self)->GetPropertyAttributes(ptr_to_local(&context), + ptr_to_local(&key)); +} + +const v8::Value* v8__Object__GetOwnPropertyDescriptor( + const v8::Object& self, const v8::Context& context, + const v8::Name& key) { + return maybe_local_to_ptr(ptr_to_local(&self)->GetOwnPropertyDescriptor( + ptr_to_local(&context), ptr_to_local(&key))); +} + + const v8::Array* v8__Array__New(v8::Isolate* isolate, int length) { return local_to_ptr(v8::Array::New(isolate, length)); } diff --git a/src/object.rs b/src/object.rs index d289f66f..530e6fc1 100644 --- a/src/object.rs +++ b/src/object.rs @@ -1,6 +1,7 @@ use crate::isolate::Isolate; use crate::support::int; use crate::support::MapFnTo; +use crate::support::Maybe; use crate::support::MaybeBool; use crate::AccessorConfiguration; use crate::AccessorNameGetterCallback; @@ -180,6 +181,17 @@ extern "C" { context: *const Context, key: *const Private, ) -> MaybeBool; + fn v8__Object__GetPropertyAttributes( + this: *const Object, + context: *const Context, + key: *const Value, + out: *mut Maybe, + ); + fn v8__Object__GetOwnPropertyDescriptor( + this: *const Object, + context: *const Context, + key: *const Name, + ) -> *const Value; fn v8__Array__New(isolate: *mut Isolate, length: int) -> *const Array; fn v8__Array__New_with_elements( @@ -755,6 +767,46 @@ impl Object { } .into() } + + /// Gets the property attributes of a property which can be + /// [PropertyAttribute::NONE] or any combination of + /// [PropertyAttribute::READ_ONLY], [PropertyAttribute::DONT_ENUM] and + /// [PropertyAttribute::DONT_DELETE]. + /// Returns [PropertyAttribute::NONE] when the property doesn't exist. + pub fn get_property_attributes( + &self, + scope: &mut HandleScope, + key: Local, + ) -> Option { + let mut out = Maybe::::default(); + unsafe { + v8__Object__GetPropertyAttributes( + self, + &*scope.get_current_context(), + &*key, + &mut out, + ) + }; + out.into() + } + + /// Implements Object.getOwnPropertyDescriptor(O, P), see + /// https://tc39.es/ecma262/#sec-object.getownpropertydescriptor. + pub fn get_own_property_descriptor<'s>( + &self, + scope: &mut HandleScope<'s>, + key: Local, + ) -> Option> { + unsafe { + scope.cast_local(|sd| { + v8__Object__GetOwnPropertyDescriptor( + self, + sd.get_current_context(), + &*key, + ) + }) + } + } } /// Object integrity levels can be used to restrict what can be done to an diff --git a/src/property_attribute.rs b/src/property_attribute.rs index 191dabd6..e11985e6 100644 --- a/src/property_attribute.rs +++ b/src/property_attribute.rs @@ -48,6 +48,11 @@ impl PropertyAttribute { let Self(rhs) = that; 0 != lhs & rhs } + + pub fn as_u32(&self) -> u32 { + let Self(bits) = self; + *bits + } } // Identical to #[derive(Default)] but arguably clearer when made explicit. diff --git a/tests/test_api.rs b/tests/test_api.rs index abba0292..7880a7af 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -6129,6 +6129,83 @@ fn get_constructor_name() { check_ctor_name(scope, "proto", "Parent"); } +#[test] +fn get_property_attributes() { + let _setup_guard = setup::parallel_test(); + let isolate = &mut v8::Isolate::new(Default::default()); + let scope = &mut v8::HandleScope::new(isolate); + let context = v8::Context::new(scope); + let scope = &mut v8::ContextScope::new(scope, context); + + let obj = eval(scope, "({ a: 1 })").unwrap(); + let obj = obj.to_object(scope).unwrap(); + let key = v8::String::new(scope, "a").unwrap(); + let attrs = obj.get_property_attributes(scope, key.into()).unwrap(); + assert!(!attrs.is_read_only()); + assert!(!attrs.is_dont_enum()); + assert!(!attrs.is_dont_delete()); + assert!(attrs.is_none()); + + // doesn't exist + let key = v8::String::new(scope, "b").unwrap(); + let attrs = obj.get_property_attributes(scope, key.into()).unwrap(); + assert!(attrs.is_none()); + + // exception + let key = eval(scope, "({ toString() { throw 'foo' } })").unwrap(); + let tc = &mut v8::TryCatch::new(scope); + assert!(obj.get_property_attributes(tc, key).is_none()); + assert!(tc.has_caught()); +} + +#[test] +fn get_own_property_descriptor() { + let _setup_guard = setup::parallel_test(); + let isolate = &mut v8::Isolate::new(Default::default()); + let scope = &mut v8::HandleScope::new(isolate); + let context = v8::Context::new(scope); + let scope = &mut v8::ContextScope::new(scope, context); + + let obj = eval(scope, "({ a: 1 })").unwrap(); + let obj = obj.to_object(scope).unwrap(); + let key = v8::String::new(scope, "a").unwrap(); + let desc = obj.get_own_property_descriptor(scope, key.into()).unwrap(); + let desc = desc.to_object(scope).unwrap(); + + let value_key = v8::String::new(scope, "value").unwrap(); + let value = desc.get(scope, value_key.into()).unwrap(); + assert!(value.is_number()); + + let writable_key = v8::String::new(scope, "writable").unwrap(); + let writable = desc.get(scope, writable_key.into()).unwrap(); + assert!(writable.is_boolean()); + assert!(writable.boolean_value(scope)); + + let enumerable_key = v8::String::new(scope, "enumerable").unwrap(); + let enumerable = desc.get(scope, enumerable_key.into()).unwrap(); + assert!(enumerable.is_boolean()); + assert!(enumerable.boolean_value(scope)); + + let configurable_key = v8::String::new(scope, "configurable").unwrap(); + let configurable = desc.get(scope, configurable_key.into()).unwrap(); + assert!(configurable.is_boolean()); + + let get_key = v8::String::new(scope, "get").unwrap(); + let get = desc.get(scope, get_key.into()).unwrap(); + assert!(get.is_undefined()); + + let set_key = v8::String::new(scope, "set").unwrap(); + let set = desc.get(scope, set_key.into()).unwrap(); + assert!(set.is_undefined()); + + // doesn't exist + let b_key = v8::String::new(scope, "b").unwrap(); + let desc = obj + .get_own_property_descriptor(scope, b_key.into()) + .unwrap(); + assert!(desc.is_undefined()); +} + #[test] fn test_prototype_api() { let _setup_guard = setup::parallel_test();