From e7f96ac7081472d38bbbdf1c8395eb31e5fe53fc Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Fri, 26 May 2023 13:14:18 +0200 Subject: [PATCH] Improved ObjectTemplate::set_*_handlers (#1237) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prior to this commit, `v8::NamedPropertyHandlerConfiguration` and `v8::IndexedPropertyHandlerConfiguration` did not expose the `definer` hook, or `flags`. This commit adds these options. In the process of doing this a couple of other changes were made: - Bitflag enum consts are now member consts of the related struct. This is done because PropertyHandlerFlags has conflicts with PropertyAttribute. - PropertyDescriptor gets all C++ introspection methods exposed to Rust. - NamedPropertyHandlerConfiguration callback types get rustdoc comments. - IndexedPropertyHandlerConfiguration callback types get rustdoc comments. - GenericNamedPropertySetterCallback gets a ReturnValue parameter, to signal trap passthrough. Co-authored-by: Bartek IwaƄczuk --- src/binding.cc | 70 +++- src/external_references.rs | 17 +- src/function.rs | 133 +++++-- src/gc.rs | 35 +- src/get_property_names_args_builder.rs | 8 +- src/lib.rs | 2 + src/property_attribute.rs | 85 ++--- src/property_descriptor.rs | 77 +++++ src/property_filter.rs | 121 +++---- src/property_handler_flags.rs | 136 ++++++++ src/template.rs | 244 ++++++++++--- tests/test_api.rs | 457 ++++++++++++++++++++++--- 12 files changed, 1141 insertions(+), 244 deletions(-) create mode 100644 src/property_handler_flags.rs diff --git a/src/binding.cc b/src/binding.cc index eba421e2..2ae30fcf 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -1148,11 +1148,13 @@ void v8__ObjectTemplate__SetNamedPropertyHandler( v8::GenericNamedPropertyQueryCallback query, v8::GenericNamedPropertyDeleterCallback deleter, v8::GenericNamedPropertyEnumeratorCallback enumerator, + v8::GenericNamedPropertyDefinerCallback definer, v8::GenericNamedPropertyDescriptorCallback descriptor, - const v8::Value* data_or_null) { + const v8::Value* data_or_null, + v8::PropertyHandlerFlags flags) { ptr_to_local(&self)->SetHandler(v8::NamedPropertyHandlerConfiguration( - getter, setter, query, deleter, enumerator, nullptr, descriptor, - ptr_to_local(data_or_null))); + getter, setter, query, deleter, enumerator, definer, descriptor, + ptr_to_local(data_or_null), flags)); } void v8__ObjectTemplate__SetIndexedPropertyHandler( @@ -1161,11 +1163,13 @@ void v8__ObjectTemplate__SetIndexedPropertyHandler( v8::IndexedPropertyQueryCallback query, v8::IndexedPropertyDeleterCallback deleter, v8::IndexedPropertyEnumeratorCallback enumerator, + v8::IndexedPropertyDefinerCallback definer, v8::IndexedPropertyDescriptorCallback descriptor, - const v8::Value* data_or_null) { + const v8::Value* data_or_null, + v8::PropertyHandlerFlags flags) { ptr_to_local(&self)->SetHandler(v8::IndexedPropertyHandlerConfiguration( - getter, setter, query, deleter, enumerator, nullptr, descriptor, - ptr_to_local(data_or_null))); + getter, setter, query, deleter, enumerator, definer, descriptor, + ptr_to_local(data_or_null), flags)); } void v8__ObjectTemplate__SetAccessorProperty(const v8::ObjectTemplate& self, @@ -3357,6 +3361,60 @@ void v8__PropertyDescriptor__DESTRUCT(v8::PropertyDescriptor* self) { self->~PropertyDescriptor(); } +bool v8__PropertyDescriptor__configurable(const v8::PropertyDescriptor* self) { + return self->configurable(); +} + +bool v8__PropertyDescriptor__enumerable(const v8::PropertyDescriptor* self) { + return self->enumerable(); +} + +bool v8__PropertyDescriptor__writable(const v8::PropertyDescriptor* self) { + return self->writable(); +} + +const v8::Value* v8__PropertyDescriptor__value( + const v8::PropertyDescriptor* self) { + return local_to_ptr(self->value()); +} + +const v8::Value* v8__PropertyDescriptor__get( + const v8::PropertyDescriptor* self) { + return local_to_ptr(self->get()); +} + +const v8::Value* v8__PropertyDescriptor__set( + const v8::PropertyDescriptor* self) { + return local_to_ptr(self->set()); +} + +bool v8__PropertyDescriptor__has_configurable( + const v8::PropertyDescriptor* self) { + return self->has_configurable(); +} + +bool v8__PropertyDescriptor__has_enumerable( + const v8::PropertyDescriptor* self) { + return self->has_enumerable(); +} + +bool v8__PropertyDescriptor__has_writable( + const v8::PropertyDescriptor* self) { + return self->has_writable(); +} + +bool v8__PropertyDescriptor__has_value(const v8::PropertyDescriptor* self) { + return self->has_value(); +} + +bool v8__PropertyDescriptor__has_get(const v8::PropertyDescriptor* self) { + return self->has_get(); +} + +bool v8__PropertyDescriptor__has_set(const v8::PropertyDescriptor* self) { + return self->has_set(); +} + void v8__PropertyDescriptor__set_enumerable(v8::PropertyDescriptor* self, bool enumurable) { self->set_enumerable(enumurable); diff --git a/src/external_references.rs b/src/external_references.rs index 24a9c2ab..6e8441f5 100644 --- a/src/external_references.rs +++ b/src/external_references.rs @@ -1,14 +1,27 @@ // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. + use crate::support::intptr_t; -use crate::AccessorNameGetterCallback; use crate::FunctionCallback; +use crate::IndexedDefinerCallback; +use crate::IndexedGetterCallback; +use crate::IndexedSetterCallback; use crate::MessageCallback; +use crate::NamedDefinerCallback; +use crate::NamedGetterCallback; +use crate::NamedSetterCallback; +use crate::PropertyEnumeratorCallback; use std::ffi::c_void; #[derive(Clone, Copy)] pub union ExternalReference<'s> { pub function: FunctionCallback, - pub getter: AccessorNameGetterCallback<'s>, + pub named_getter: NamedGetterCallback<'s>, + pub named_setter: NamedSetterCallback<'s>, + pub named_definer: NamedDefinerCallback<'s>, + pub indexed_getter: IndexedGetterCallback<'s>, + pub indexed_setter: IndexedSetterCallback<'s>, + pub indexed_definer: IndexedDefinerCallback<'s>, + pub enumerator: PropertyEnumeratorCallback<'s>, pub message: MessageCallback, pub pointer: *mut c_void, } diff --git a/src/function.rs b/src/function.rs index 7d348d40..b5fc8483 100644 --- a/src/function.rs +++ b/src/function.rs @@ -18,6 +18,7 @@ use crate::Isolate; use crate::Local; use crate::Name; use crate::Object; +use crate::PropertyDescriptor; use crate::Signature; use crate::String; use crate::UniqueRef; @@ -121,7 +122,7 @@ impl<'cb> ReturnValue<'cb> { } #[inline(always)] - fn from_property_callback_info(info: &'cb PropertyCallbackInfo) -> Self { + pub fn from_property_callback_info(info: &'cb PropertyCallbackInfo) -> Self { let nn = info.get_return_value_non_null(); Self(nn, PhantomData) } @@ -521,10 +522,10 @@ impl<'s> PropertyCallbackArguments<'s> { pub type FunctionCallback = extern "C" fn(*const FunctionCallbackInfo); -impl<'a, F> MapFnFrom for FunctionCallback +impl MapFnFrom for FunctionCallback where F: UnitType - + Fn(&mut HandleScope<'a>, FunctionCallbackArguments<'a>, ReturnValue), + + for<'s> Fn(&mut HandleScope<'s>, FunctionCallbackArguments<'s>, ReturnValue), { fn mapping() -> Self { let f = |info: *const FunctionCallbackInfo| { @@ -538,15 +539,18 @@ where } } -/// AccessorNameGetterCallback is used as callback functions when getting a -/// particular property. See Object and ObjectTemplate's method SetAccessor. -pub type AccessorNameGetterCallback<'s> = +pub(crate) type NamedGetterCallback<'s> = extern "C" fn(Local<'s, Name>, *const PropertyCallbackInfo); -impl MapFnFrom for AccessorNameGetterCallback<'_> +impl MapFnFrom for NamedGetterCallback<'_> where F: UnitType - + Fn(&mut HandleScope, Local, PropertyCallbackArguments, ReturnValue), + + for<'s> Fn( + &mut HandleScope<'s>, + Local<'s, Name>, + PropertyCallbackArguments<'s>, + ReturnValue, + ), { fn mapping() -> Self { let f = |key: Local, info: *const PropertyCallbackInfo| { @@ -560,13 +564,19 @@ where } } -pub type AccessorNameSetterCallback<'s> = +pub(crate) type NamedSetterCallback<'s> = extern "C" fn(Local<'s, Name>, Local<'s, Value>, *const PropertyCallbackInfo); -impl MapFnFrom for AccessorNameSetterCallback<'_> +impl MapFnFrom for NamedSetterCallback<'_> where F: UnitType - + Fn(&mut HandleScope, Local, Local, PropertyCallbackArguments), + + for<'s> Fn( + &mut HandleScope<'s>, + Local<'s, Name>, + Local<'s, Value>, + PropertyCallbackArguments<'s>, + ReturnValue, + ), { fn mapping() -> Self { let f = |key: Local, @@ -575,19 +585,21 @@ where let info = unsafe { &*info }; let scope = &mut unsafe { CallbackScope::new(info) }; let args = PropertyCallbackArguments::from_property_callback_info(info); - (F::get())(scope, key, value, args); + let rv = ReturnValue::from_property_callback_info(info); + (F::get())(scope, key, value, args, rv); }; f.to_c_fn() } } -//Should return an Array in Return Value -pub type PropertyEnumeratorCallback<'s> = +// Should return an Array in Return Value +pub(crate) type PropertyEnumeratorCallback<'s> = extern "C" fn(*const PropertyCallbackInfo); impl MapFnFrom for PropertyEnumeratorCallback<'_> where - F: UnitType + Fn(&mut HandleScope, PropertyCallbackArguments, ReturnValue), + F: UnitType + + for<'s> Fn(&mut HandleScope<'s>, PropertyCallbackArguments<'s>, ReturnValue), { fn mapping() -> Self { let f = |info: *const PropertyCallbackInfo| { @@ -601,15 +613,50 @@ where } } -/// IndexedPropertyGetterCallback is used as callback functions when registering a named handler -/// particular property. See Object and ObjectTemplate's method SetHandler. -pub type IndexedPropertyGetterCallback<'s> = - extern "C" fn(u32, *const PropertyCallbackInfo); +pub(crate) type NamedDefinerCallback<'s> = extern "C" fn( + Local<'s, Name>, + *const PropertyDescriptor, + *const PropertyCallbackInfo, +); -impl MapFnFrom for IndexedPropertyGetterCallback<'_> +impl MapFnFrom for NamedDefinerCallback<'_> where F: UnitType - + Fn(&mut HandleScope, u32, PropertyCallbackArguments, ReturnValue), + + for<'s> Fn( + &mut HandleScope<'s>, + Local<'s, Name>, + &PropertyDescriptor, + PropertyCallbackArguments<'s>, + ReturnValue, + ), +{ + fn mapping() -> Self { + let f = |key: Local, + desc: *const PropertyDescriptor, + info: *const PropertyCallbackInfo| { + let info = unsafe { &*info }; + let scope = &mut unsafe { CallbackScope::new(info) }; + let args = PropertyCallbackArguments::from_property_callback_info(info); + let desc = unsafe { &*desc }; + let rv = ReturnValue::from_property_callback_info(info); + (F::get())(scope, key, desc, args, rv); + }; + f.to_c_fn() + } +} + +pub(crate) type IndexedGetterCallback<'s> = + extern "C" fn(u32, *const PropertyCallbackInfo); + +impl MapFnFrom for IndexedGetterCallback<'_> +where + F: UnitType + + for<'s> Fn( + &mut HandleScope<'s>, + u32, + PropertyCallbackArguments<'s>, + ReturnValue, + ), { fn mapping() -> Self { let f = |index: u32, info: *const PropertyCallbackInfo| { @@ -623,13 +670,19 @@ where } } -pub type IndexedPropertySetterCallback<'s> = +pub(crate) type IndexedSetterCallback<'s> = extern "C" fn(u32, Local<'s, Value>, *const PropertyCallbackInfo); -impl MapFnFrom for IndexedPropertySetterCallback<'_> +impl MapFnFrom for IndexedSetterCallback<'_> where F: UnitType - + Fn(&mut HandleScope, u32, Local, PropertyCallbackArguments), + + for<'s> Fn( + &mut HandleScope<'s>, + u32, + Local<'s, Value>, + PropertyCallbackArguments<'s>, + ReturnValue, + ), { fn mapping() -> Self { let f = @@ -637,12 +690,42 @@ where let info = unsafe { &*info }; let scope = &mut unsafe { CallbackScope::new(info) }; let args = PropertyCallbackArguments::from_property_callback_info(info); - (F::get())(scope, index, value, args); + let rv = ReturnValue::from_property_callback_info(info); + (F::get())(scope, index, value, args, rv); }; f.to_c_fn() } } +pub(crate) type IndexedDefinerCallback<'s> = + extern "C" fn(u32, *const PropertyDescriptor, *const PropertyCallbackInfo); + +impl MapFnFrom for IndexedDefinerCallback<'_> +where + F: UnitType + + for<'s> Fn( + &mut HandleScope<'s>, + u32, + &PropertyDescriptor, + PropertyCallbackArguments<'s>, + ReturnValue, + ), +{ + fn mapping() -> Self { + let f = |index: u32, + desc: *const PropertyDescriptor, + info: *const PropertyCallbackInfo| { + let info = unsafe { &*info }; + let scope = &mut unsafe { CallbackScope::new(info) }; + let args = PropertyCallbackArguments::from_property_callback_info(info); + let rv = ReturnValue::from_property_callback_info(info); + let desc = unsafe { &*desc }; + (F::get())(scope, index, desc, args, rv); + }; + f.to_c_fn() + } +} + /// A builder to construct the properties of a Function or FunctionTemplate. pub struct FunctionBuilder<'s, T> { pub(crate) callback: FunctionCallback, diff --git a/src/gc.rs b/src/gc.rs index 59069bfb..f79f1ae7 100644 --- a/src/gc.rs +++ b/src/gc.rs @@ -7,17 +7,19 @@ #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub struct GCType(u32); -pub const GC_TYPE_TYPE_SCAVENGE: GCType = GCType(1); +impl GCType { + pub const SCAVENGE: Self = Self(1 << 0); -pub const GC_TYPE_MINOR_MARK_COMPACT: GCType = GCType(2); + pub const MINOR_MARK_COMPACT: Self = Self(1 << 1); -pub const GC_TYPE_MARK_SWEEP_COMPACT: GCType = GCType(4); + pub const MARK_SWEEP_COMPACT: Self = Self(1 << 2); -pub const GC_TYPE_INCREMENTAL_MARKING: GCType = GCType(8); + pub const INCREMENTAL_MARKING: Self = Self(1 << 3); -pub const GC_TYPE_PROCESS_WEAK_CALLBACK: GCType = GCType(16); + pub const PROCESS_WEAK_CALLBACKS: Self = Self(1 << 4); -pub const GC_TYPE_ALL: GCType = GCType(31); + pub const ALL: Self = Self(31); +} impl std::ops::BitOr for GCType { type Output = Self; @@ -44,24 +46,21 @@ impl std::ops::BitOr for GCType { #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub struct GCCallbackFlags(u32); -pub const GC_CALLBACK_FLAGS_NO_FLAGS: GCCallbackFlags = GCCallbackFlags(0); +impl GCCallbackFlags { + pub const NONE: Self = Self(0); -pub const GC_CALLBACK_FLAGS_CONSTRUCT_RETAINED_OBJECT_INFOS: GCCallbackFlags = - GCCallbackFlags(2); + pub const CONSTRUCT_RETAINED_OBJECT_INFOS: Self = Self(1 << 1); -pub const GC_CALLBACK_FLAGS_FORCED: GCCallbackFlags = GCCallbackFlags(4); + pub const FORCED: Self = Self(1 << 2); -pub const GC_CALLBACK_FLAGS_SYNCHRONOUS_PHANTOM_CALLBACK_PROCESSING: - GCCallbackFlags = GCCallbackFlags(8); + pub const SYNCHRONOUS_PHANTOM_CALLBACK_PROCESSING: Self = Self(1 << 3); -pub const GC_CALLBACK_FLAGS_COLLECT_ALL_AVAILABLE_GARBAGE: GCCallbackFlags = - GCCallbackFlags(16); + pub const COLLECT_ALL_AVAILABLE_GARBAGE: Self = Self(1 << 4); -pub const GC_CALLBACK_FLAGS_COLLECT_ALL_EXTERNAL_MEMORY: GCCallbackFlags = - GCCallbackFlags(32); + pub const COLLECT_ALL_EXTERNAL_MEMORY: Self = Self(1 << 5); -pub const GC_CALLBACK_FLAGS_SCHEDULE_IDLE_GARBAGE_COLLECTION: GCCallbackFlags = - GCCallbackFlags(64); + pub const SCHEDULE_IDLE_GARBAGE_COLLECTION: Self = Self(1 << 6); +} impl std::ops::BitOr for GCCallbackFlags { type Output = Self; diff --git a/src/get_property_names_args_builder.rs b/src/get_property_names_args_builder.rs index cfd33cc8..dcd26d32 100644 --- a/src/get_property_names_args_builder.rs +++ b/src/get_property_names_args_builder.rs @@ -1,6 +1,4 @@ use crate::PropertyFilter; -use crate::ONLY_ENUMERABLE; -use crate::SKIP_SYMBOLS; #[derive(Debug, Clone, Copy)] #[repr(C)] @@ -47,7 +45,8 @@ impl Default for GetPropertyNamesArgs { fn default() -> Self { GetPropertyNamesArgs { mode: KeyCollectionMode::IncludePrototypes, - property_filter: ONLY_ENUMERABLE | SKIP_SYMBOLS, + property_filter: PropertyFilter::ONLY_ENUMERABLE + | PropertyFilter::SKIP_SYMBOLS, index_filter: IndexFilter::IncludeIndices, key_conversion: KeyConversionMode::KeepNumbers, } @@ -72,7 +71,8 @@ impl GetPropertyNamesArgsBuilder { pub fn new() -> Self { Self { mode: KeyCollectionMode::IncludePrototypes, - property_filter: ONLY_ENUMERABLE | SKIP_SYMBOLS, + property_filter: PropertyFilter::ONLY_ENUMERABLE + | PropertyFilter::SKIP_SYMBOLS, index_filter: IndexFilter::IncludeIndices, key_conversion: KeyConversionMode::KeepNumbers, } diff --git a/src/lib.rs b/src/lib.rs index 1255ee1c..6b59205b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,7 @@ mod promise; mod property_attribute; mod property_descriptor; mod property_filter; +mod property_handler_flags; mod proxy; mod scope; mod script; @@ -126,6 +127,7 @@ pub use promise::{PromiseRejectEvent, PromiseRejectMessage, PromiseState}; pub use property_attribute::*; pub use property_descriptor::*; pub use property_filter::*; +pub use property_handler_flags::*; pub use proxy::*; pub use scope::CallbackScope; pub use scope::ContextScope; diff --git a/src/property_attribute.rs b/src/property_attribute.rs index e11985e6..c980d658 100644 --- a/src/property_attribute.rs +++ b/src/property_attribute.rs @@ -2,44 +2,44 @@ #[derive(Debug, Eq, PartialEq)] pub struct PropertyAttribute(u32); -/// No property attributes. -pub const NONE: PropertyAttribute = PropertyAttribute(0); - -/// Not writable. Corresponds to -/// `Object.defineProperty(o, "p", { writable: false })`. -pub const READ_ONLY: PropertyAttribute = PropertyAttribute(1); - -/// Not enumerable. Corresponds to -/// `Object.defineProperty(o, "p", { enumerable: false })`. -pub const DONT_ENUM: PropertyAttribute = PropertyAttribute(2); - -/// Not configurable. Corresponds to -/// `Object.defineProperty(o, "p", { configurable: false })`. -pub const DONT_DELETE: PropertyAttribute = PropertyAttribute(4); - impl PropertyAttribute { + /// No property attributes. + pub const NONE: Self = Self(0); + + /// Not writable. Corresponds to + /// `Object.defineProperty(o, "p", { writable: false })`. + pub const READ_ONLY: Self = Self(1 << 0); + + /// Not enumerable. Corresponds to + /// `Object.defineProperty(o, "p", { enumerable: false })`. + pub const DONT_ENUM: Self = Self(1 << 1); + + /// Not configurable. Corresponds to + /// `Object.defineProperty(o, "p", { configurable: false })`. + pub const DONT_DELETE: Self = Self(1 << 2); + /// Test if no property attributes are set. #[inline(always)] pub fn is_none(&self) -> bool { - *self == NONE + *self == PropertyAttribute::NONE } /// Test if the read-only property attribute is set. #[inline(always)] pub fn is_read_only(&self) -> bool { - self.has(READ_ONLY) + self.has(Self::READ_ONLY) } /// Test if the non-enumerable property attribute is set. #[inline(always)] pub fn is_dont_enum(&self) -> bool { - self.has(DONT_ENUM) + self.has(Self::DONT_ENUM) } /// Test if the non-configurable property attribute is set. #[inline(always)] pub fn is_dont_delete(&self) -> bool { - self.has(DONT_DELETE) + self.has(Self::DONT_DELETE) } #[inline(always)] @@ -58,7 +58,7 @@ impl PropertyAttribute { // Identical to #[derive(Default)] but arguably clearer when made explicit. impl Default for PropertyAttribute { fn default() -> Self { - NONE + Self::NONE } } @@ -73,36 +73,41 @@ impl std::ops::BitOr for PropertyAttribute { #[test] fn test_attr() { - assert!(NONE.is_none()); - assert!(!NONE.is_read_only()); - assert!(!NONE.is_dont_enum()); - assert!(!NONE.is_dont_delete()); + assert!(PropertyAttribute::NONE.is_none()); + assert!(!PropertyAttribute::NONE.is_read_only()); + assert!(!PropertyAttribute::NONE.is_dont_enum()); + assert!(!PropertyAttribute::NONE.is_dont_delete()); - assert!(!READ_ONLY.is_none()); - assert!(READ_ONLY.is_read_only()); - assert!(!READ_ONLY.is_dont_enum()); - assert!(!READ_ONLY.is_dont_delete()); + assert!(!PropertyAttribute::READ_ONLY.is_none()); + assert!(PropertyAttribute::READ_ONLY.is_read_only()); + assert!(!PropertyAttribute::READ_ONLY.is_dont_enum()); + assert!(!PropertyAttribute::READ_ONLY.is_dont_delete()); - assert!(!DONT_ENUM.is_none()); - assert!(!DONT_ENUM.is_read_only()); - assert!(DONT_ENUM.is_dont_enum()); - assert!(!DONT_ENUM.is_dont_delete()); + assert!(!PropertyAttribute::DONT_ENUM.is_none()); + assert!(!PropertyAttribute::DONT_ENUM.is_read_only()); + assert!(PropertyAttribute::DONT_ENUM.is_dont_enum()); + assert!(!PropertyAttribute::DONT_ENUM.is_dont_delete()); - assert!(!DONT_DELETE.is_none()); - assert!(!DONT_DELETE.is_read_only()); - assert!(!DONT_DELETE.is_dont_enum()); - assert!(DONT_DELETE.is_dont_delete()); + assert!(!PropertyAttribute::DONT_DELETE.is_none()); + assert!(!PropertyAttribute::DONT_DELETE.is_read_only()); + assert!(!PropertyAttribute::DONT_DELETE.is_dont_enum()); + assert!(PropertyAttribute::DONT_DELETE.is_dont_delete()); - assert_eq!(NONE, Default::default()); - assert_eq!(READ_ONLY, NONE | READ_ONLY); + assert_eq!(PropertyAttribute::NONE, Default::default()); + assert_eq!( + PropertyAttribute::READ_ONLY, + PropertyAttribute::NONE | PropertyAttribute::READ_ONLY + ); - let attr = READ_ONLY | DONT_ENUM; + let attr = PropertyAttribute::READ_ONLY | PropertyAttribute::DONT_ENUM; assert!(!attr.is_none()); assert!(attr.is_read_only()); assert!(attr.is_dont_enum()); assert!(!attr.is_dont_delete()); - let attr = READ_ONLY | READ_ONLY | DONT_ENUM; + let attr = PropertyAttribute::READ_ONLY + | PropertyAttribute::READ_ONLY + | PropertyAttribute::DONT_ENUM; assert!(!attr.is_none()); assert!(attr.is_read_only()); assert!(attr.is_dont_enum()); diff --git a/src/property_descriptor.rs b/src/property_descriptor.rs index f9f02e65..446be66c 100644 --- a/src/property_descriptor.rs +++ b/src/property_descriptor.rs @@ -21,6 +21,35 @@ extern "C" { set: *const Value, ); fn v8__PropertyDescriptor__DESTRUCT(this: *mut PropertyDescriptor); + fn v8__PropertyDescriptor__configurable( + this: *const PropertyDescriptor, + ) -> bool; + fn v8__PropertyDescriptor__enumerable( + this: *const PropertyDescriptor, + ) -> bool; + fn v8__PropertyDescriptor__writable(this: *const PropertyDescriptor) -> bool; + fn v8__PropertyDescriptor__value( + this: *const PropertyDescriptor, + ) -> *const Value; + fn v8__PropertyDescriptor__get( + this: *const PropertyDescriptor, + ) -> *const Value; + fn v8__PropertyDescriptor__set( + this: *const PropertyDescriptor, + ) -> *const Value; + fn v8__PropertyDescriptor__has_configurable( + this: *const PropertyDescriptor, + ) -> bool; + fn v8__PropertyDescriptor__has_enumerable( + this: *const PropertyDescriptor, + ) -> bool; + fn v8__PropertyDescriptor__has_writable( + this: *const PropertyDescriptor, + ) -> bool; + fn v8__PropertyDescriptor__has_value(this: *const PropertyDescriptor) + -> bool; + fn v8__PropertyDescriptor__has_get(this: *const PropertyDescriptor) -> bool; + fn v8__PropertyDescriptor__has_set(this: *const PropertyDescriptor) -> bool; fn v8__PropertyDescriptor__set_enumerable( this: *mut PropertyDescriptor, enumerable: bool, @@ -88,6 +117,54 @@ impl PropertyDescriptor { } } + pub fn configurable(&self) -> bool { + unsafe { v8__PropertyDescriptor__configurable(self) } + } + + pub fn enumerable(&self) -> bool { + unsafe { v8__PropertyDescriptor__enumerable(self) } + } + + pub fn writable(&self) -> bool { + unsafe { v8__PropertyDescriptor__writable(self) } + } + + pub fn value(&self) -> Local { + unsafe { Local::from_raw(v8__PropertyDescriptor__value(self)) }.unwrap() + } + + pub fn get(&self) -> Local { + unsafe { Local::from_raw(v8__PropertyDescriptor__get(self)) }.unwrap() + } + + pub fn set(&self) -> Local { + unsafe { Local::from_raw(v8__PropertyDescriptor__set(self)) }.unwrap() + } + + pub fn has_configurable(&self) -> bool { + unsafe { v8__PropertyDescriptor__has_configurable(self) } + } + + pub fn has_enumerable(&self) -> bool { + unsafe { v8__PropertyDescriptor__has_enumerable(self) } + } + + pub fn has_writable(&self) -> bool { + unsafe { v8__PropertyDescriptor__has_writable(self) } + } + + pub fn has_value(&self) -> bool { + unsafe { v8__PropertyDescriptor__has_value(self) } + } + + pub fn has_get(&self) -> bool { + unsafe { v8__PropertyDescriptor__has_get(self) } + } + + pub fn has_set(&self) -> bool { + unsafe { v8__PropertyDescriptor__has_set(self) } + } + pub fn set_enumerable(&mut self, enumerable: bool) { unsafe { v8__PropertyDescriptor__set_enumerable(self, enumerable) } } diff --git a/src/property_filter.rs b/src/property_filter.rs index d26506a5..0268a69d 100644 --- a/src/property_filter.rs +++ b/src/property_filter.rs @@ -2,53 +2,53 @@ #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub struct PropertyFilter(u32); -pub const ALL_PROPERTIES: PropertyFilter = PropertyFilter(0); - -pub const ONLY_WRITABLE: PropertyFilter = PropertyFilter(1); - -pub const ONLY_ENUMERABLE: PropertyFilter = PropertyFilter(2); - -pub const ONLY_CONFIGURABLE: PropertyFilter = PropertyFilter(4); - -pub const SKIP_STRINGS: PropertyFilter = PropertyFilter(8); - -pub const SKIP_SYMBOLS: PropertyFilter = PropertyFilter(16); - impl PropertyFilter { + pub const ALL_PROPERTIES: PropertyFilter = PropertyFilter(0); + + pub const ONLY_WRITABLE: PropertyFilter = PropertyFilter(1 << 0); + + pub const ONLY_ENUMERABLE: PropertyFilter = PropertyFilter(1 << 1); + + pub const ONLY_CONFIGURABLE: PropertyFilter = PropertyFilter(1 << 2); + + pub const SKIP_STRINGS: PropertyFilter = PropertyFilter(1 << 3); + + pub const SKIP_SYMBOLS: PropertyFilter = PropertyFilter(1 << 4); + /// Test if all property filters are set. #[inline(always)] pub fn is_all_properties(&self) -> bool { - *self == ALL_PROPERTIES + *self == Self::ALL_PROPERTIES } /// Test if the only-writable property filter is set. #[inline(always)] pub fn is_only_writable(&self) -> bool { - self.has(ONLY_WRITABLE) + self.has(Self::ONLY_WRITABLE) } /// Test if the only-enumerable property filter is set. #[inline(always)] pub fn is_only_enumerable(&self) -> bool { - self.has(ONLY_ENUMERABLE) + self.has(Self::ONLY_ENUMERABLE) } /// Test if the only-configurable property filter is set. #[inline(always)] pub fn is_only_configurable(&self) -> bool { - self.has(ONLY_CONFIGURABLE) + self.has(Self::ONLY_CONFIGURABLE) } /// Test if the skip-strings property filter is set. #[inline(always)] pub fn is_skip_strings(&self) -> bool { - self.has(SKIP_STRINGS) + self.has(Self::SKIP_STRINGS) } /// Test if the skip-symbols property filter is set. #[inline(always)] pub fn is_skip_symbols(&self) -> bool { - self.has(SKIP_SYMBOLS) + self.has(Self::SKIP_SYMBOLS) } #[inline(always)] @@ -62,7 +62,7 @@ impl PropertyFilter { // Identical to #[derive(Default)] but arguably clearer when made explicit. impl Default for PropertyFilter { fn default() -> Self { - ALL_PROPERTIES + Self::ALL_PROPERTIES } } @@ -77,52 +77,57 @@ impl std::ops::BitOr for PropertyFilter { #[test] fn test_attr() { - assert!(ALL_PROPERTIES.is_all_properties()); - assert!(!ALL_PROPERTIES.is_only_writable()); - assert!(!ALL_PROPERTIES.is_only_enumerable()); - assert!(!ALL_PROPERTIES.is_only_configurable()); - assert!(!ALL_PROPERTIES.is_skip_strings()); - assert!(!ALL_PROPERTIES.is_skip_symbols()); + assert!(PropertyFilter::ALL_PROPERTIES.is_all_properties()); + assert!(!PropertyFilter::ALL_PROPERTIES.is_only_writable()); + assert!(!PropertyFilter::ALL_PROPERTIES.is_only_enumerable()); + assert!(!PropertyFilter::ALL_PROPERTIES.is_only_configurable()); + assert!(!PropertyFilter::ALL_PROPERTIES.is_skip_strings()); + assert!(!PropertyFilter::ALL_PROPERTIES.is_skip_symbols()); - assert!(!ONLY_WRITABLE.is_all_properties()); - assert!(ONLY_WRITABLE.is_only_writable()); - assert!(!ONLY_WRITABLE.is_only_enumerable()); - assert!(!ONLY_WRITABLE.is_only_configurable()); - assert!(!ONLY_WRITABLE.is_skip_strings()); - assert!(!ONLY_WRITABLE.is_skip_symbols()); + assert!(!PropertyFilter::ONLY_WRITABLE.is_all_properties()); + assert!(PropertyFilter::ONLY_WRITABLE.is_only_writable()); + assert!(!PropertyFilter::ONLY_WRITABLE.is_only_enumerable()); + assert!(!PropertyFilter::ONLY_WRITABLE.is_only_configurable()); + assert!(!PropertyFilter::ONLY_WRITABLE.is_skip_strings()); + assert!(!PropertyFilter::ONLY_WRITABLE.is_skip_symbols()); - assert!(!ONLY_ENUMERABLE.is_all_properties()); - assert!(!ONLY_ENUMERABLE.is_only_writable()); - assert!(ONLY_ENUMERABLE.is_only_enumerable()); - assert!(!ONLY_ENUMERABLE.is_only_configurable()); - assert!(!ONLY_ENUMERABLE.is_skip_strings()); - assert!(!ONLY_ENUMERABLE.is_skip_symbols()); + assert!(!PropertyFilter::ONLY_ENUMERABLE.is_all_properties()); + assert!(!PropertyFilter::ONLY_ENUMERABLE.is_only_writable()); + assert!(PropertyFilter::ONLY_ENUMERABLE.is_only_enumerable()); + assert!(!PropertyFilter::ONLY_ENUMERABLE.is_only_configurable()); + assert!(!PropertyFilter::ONLY_ENUMERABLE.is_skip_strings()); + assert!(!PropertyFilter::ONLY_ENUMERABLE.is_skip_symbols()); - assert!(!ONLY_CONFIGURABLE.is_all_properties()); - assert!(!ONLY_CONFIGURABLE.is_only_writable()); - assert!(!ONLY_CONFIGURABLE.is_only_enumerable()); - assert!(ONLY_CONFIGURABLE.is_only_configurable()); - assert!(!ONLY_CONFIGURABLE.is_skip_strings()); - assert!(!ONLY_CONFIGURABLE.is_skip_symbols()); + assert!(!PropertyFilter::ONLY_CONFIGURABLE.is_all_properties()); + assert!(!PropertyFilter::ONLY_CONFIGURABLE.is_only_writable()); + assert!(!PropertyFilter::ONLY_CONFIGURABLE.is_only_enumerable()); + assert!(PropertyFilter::ONLY_CONFIGURABLE.is_only_configurable()); + assert!(!PropertyFilter::ONLY_CONFIGURABLE.is_skip_strings()); + assert!(!PropertyFilter::ONLY_CONFIGURABLE.is_skip_symbols()); - assert!(!SKIP_STRINGS.is_all_properties()); - assert!(!SKIP_STRINGS.is_only_writable()); - assert!(!SKIP_STRINGS.is_only_enumerable()); - assert!(!SKIP_STRINGS.is_only_configurable()); - assert!(SKIP_STRINGS.is_skip_strings()); - assert!(!SKIP_STRINGS.is_skip_symbols()); + assert!(!PropertyFilter::SKIP_STRINGS.is_all_properties()); + assert!(!PropertyFilter::SKIP_STRINGS.is_only_writable()); + assert!(!PropertyFilter::SKIP_STRINGS.is_only_enumerable()); + assert!(!PropertyFilter::SKIP_STRINGS.is_only_configurable()); + assert!(PropertyFilter::SKIP_STRINGS.is_skip_strings()); + assert!(!PropertyFilter::SKIP_STRINGS.is_skip_symbols()); - assert!(!SKIP_SYMBOLS.is_all_properties()); - assert!(!SKIP_SYMBOLS.is_only_writable()); - assert!(!SKIP_SYMBOLS.is_only_enumerable()); - assert!(!SKIP_SYMBOLS.is_only_configurable()); - assert!(!SKIP_SYMBOLS.is_skip_strings()); - assert!(SKIP_SYMBOLS.is_skip_symbols()); + assert!(!PropertyFilter::SKIP_SYMBOLS.is_all_properties()); + assert!(!PropertyFilter::SKIP_SYMBOLS.is_only_writable()); + assert!(!PropertyFilter::SKIP_SYMBOLS.is_only_enumerable()); + assert!(!PropertyFilter::SKIP_SYMBOLS.is_only_configurable()); + assert!(!PropertyFilter::SKIP_SYMBOLS.is_skip_strings()); + assert!(PropertyFilter::SKIP_SYMBOLS.is_skip_symbols()); - assert_eq!(ALL_PROPERTIES, Default::default()); - assert_eq!(ONLY_WRITABLE, ALL_PROPERTIES | ONLY_WRITABLE); + assert_eq!(PropertyFilter::ALL_PROPERTIES, Default::default()); + assert_eq!( + PropertyFilter::ONLY_WRITABLE, + PropertyFilter::ALL_PROPERTIES | PropertyFilter::ONLY_WRITABLE + ); - let attr = ONLY_WRITABLE | ONLY_WRITABLE | SKIP_STRINGS; + let attr = PropertyFilter::ONLY_WRITABLE + | PropertyFilter::ONLY_WRITABLE + | PropertyFilter::SKIP_STRINGS; assert!(!attr.is_all_properties()); assert!(attr.is_only_writable()); assert!(!attr.is_only_enumerable()); diff --git a/src/property_handler_flags.rs b/src/property_handler_flags.rs new file mode 100644 index 00000000..126e5a6e --- /dev/null +++ b/src/property_handler_flags.rs @@ -0,0 +1,136 @@ +// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. + +#[repr(C)] +#[derive(Debug, Eq, PartialEq)] +pub struct PropertyHandlerFlags(u32); + +impl PropertyHandlerFlags { + /// None. + pub const NONE: Self = Self(0); + + /// See ALL_CAN_READ above. + pub const ALL_CAN_READ: Self = Self(1 << 0); + + /// Will not call into interceptor for properties on the receiver or prototype + /// chain, i.e., only call into interceptor for properties that do not exist. + /// Currently only valid for named interceptors. + pub const NON_MASKING: Self = Self(1 << 1); + + /// Will not call into interceptor for symbol lookup. Only meaningful for + /// named interceptors. + pub const ONLY_INTERCEPT_STRINGS: Self = Self(1 << 2); + + /// The getter, query, enumerator callbacks do not produce side effects. + pub const HAS_NO_SIDE_EFFECT: Self = Self(1 << 3); + + /// Test if no property handler flags are set. + #[inline(always)] + pub fn is_none(&self) -> bool { + *self == Self::NONE + } + + /// Test if the all-can-read property handler flag is set. + #[inline(always)] + pub fn is_all_can_read(&self) -> bool { + self.has(Self::ALL_CAN_READ) + } + + /// Test if the non-masking property handler flag is set. + #[inline(always)] + pub fn is_non_masking(&self) -> bool { + self.has(Self::NON_MASKING) + } + + /// Test if the only-intercept-strings property handler flag is set. + #[inline(always)] + pub fn is_only_intercept_strings(&self) -> bool { + self.has(Self::ONLY_INTERCEPT_STRINGS) + } + + /// Test if the has-no-side-effect property handler flag is set. + #[inline(always)] + pub fn is_has_no_side_effect(&self) -> bool { + self.has(Self::HAS_NO_SIDE_EFFECT) + } + + #[inline(always)] + fn has(&self, that: Self) -> bool { + let Self(lhs) = self; + let Self(rhs) = that; + 0 != lhs & rhs + } +} + +// Identical to #[derive(Default)] but arguably clearer when made explicit. +impl Default for PropertyHandlerFlags { + fn default() -> Self { + Self::NONE + } +} + +impl std::ops::BitOr for PropertyHandlerFlags { + type Output = Self; + + fn bitor(self, Self(rhs): Self) -> Self { + let Self(lhs) = self; + Self(lhs | rhs) + } +} + +#[test] +fn test_attr() { + assert!(PropertyHandlerFlags::NONE.is_none()); + assert!(!PropertyHandlerFlags::NONE.is_all_can_read()); + assert!(!PropertyHandlerFlags::NONE.is_non_masking()); + assert!(!PropertyHandlerFlags::NONE.is_only_intercept_strings()); + assert!(!PropertyHandlerFlags::NONE.is_has_no_side_effect()); + + assert!(!PropertyHandlerFlags::ALL_CAN_READ.is_none()); + assert!(PropertyHandlerFlags::ALL_CAN_READ.is_all_can_read()); + assert!(!PropertyHandlerFlags::ALL_CAN_READ.is_non_masking()); + assert!(!PropertyHandlerFlags::ALL_CAN_READ.is_only_intercept_strings()); + assert!(!PropertyHandlerFlags::ALL_CAN_READ.is_has_no_side_effect()); + + assert!(!PropertyHandlerFlags::NON_MASKING.is_none()); + assert!(!PropertyHandlerFlags::NON_MASKING.is_all_can_read()); + assert!(PropertyHandlerFlags::NON_MASKING.is_non_masking()); + assert!(!PropertyHandlerFlags::NON_MASKING.is_only_intercept_strings()); + assert!(!PropertyHandlerFlags::NON_MASKING.is_has_no_side_effect()); + + assert!(!PropertyHandlerFlags::ONLY_INTERCEPT_STRINGS.is_none()); + assert!(!PropertyHandlerFlags::ONLY_INTERCEPT_STRINGS.is_all_can_read()); + assert!(!PropertyHandlerFlags::ONLY_INTERCEPT_STRINGS.is_non_masking()); + assert!( + PropertyHandlerFlags::ONLY_INTERCEPT_STRINGS.is_only_intercept_strings() + ); + assert!(!PropertyHandlerFlags::ONLY_INTERCEPT_STRINGS.is_has_no_side_effect()); + + assert!(!PropertyHandlerFlags::HAS_NO_SIDE_EFFECT.is_none()); + assert!(!PropertyHandlerFlags::HAS_NO_SIDE_EFFECT.is_all_can_read()); + assert!(!PropertyHandlerFlags::HAS_NO_SIDE_EFFECT.is_non_masking()); + assert!(!PropertyHandlerFlags::HAS_NO_SIDE_EFFECT.is_only_intercept_strings()); + assert!(PropertyHandlerFlags::HAS_NO_SIDE_EFFECT.is_has_no_side_effect()); + + assert_eq!(PropertyHandlerFlags::NONE, Default::default()); + assert_eq!( + PropertyHandlerFlags::ALL_CAN_READ, + PropertyHandlerFlags::NONE | PropertyHandlerFlags::ALL_CAN_READ + ); + + let attr = + PropertyHandlerFlags::ALL_CAN_READ | PropertyHandlerFlags::NON_MASKING; + assert!(!attr.is_none()); + assert!(attr.is_all_can_read()); + assert!(attr.is_non_masking()); + assert!(!attr.is_only_intercept_strings()); + assert!(!attr.is_has_no_side_effect()); + + let attr = PropertyHandlerFlags::ONLY_INTERCEPT_STRINGS + | PropertyHandlerFlags::HAS_NO_SIDE_EFFECT + | PropertyHandlerFlags::NON_MASKING; + assert!(!attr.is_none()); + assert!(!attr.is_all_can_read()); + assert!(attr.is_non_masking()); + assert!(attr.is_only_intercept_strings()); + assert!(attr.is_has_no_side_effect()); +} diff --git a/src/template.rs b/src/template.rs index f477f256..82a5be85 100644 --- a/src/template.rs +++ b/src/template.rs @@ -9,25 +9,27 @@ use crate::fast_api::FastFunction; use crate::isolate::Isolate; use crate::support::int; use crate::support::MapFnTo; -use crate::AccessorNameGetterCallback; -use crate::AccessorNameSetterCallback; use crate::ConstructorBehavior; use crate::Context; use crate::Function; use crate::FunctionBuilder; use crate::FunctionCallback; use crate::HandleScope; -use crate::IndexedPropertyGetterCallback; -use crate::IndexedPropertySetterCallback; +use crate::IndexedDefinerCallback; +use crate::IndexedGetterCallback; +use crate::IndexedSetterCallback; use crate::Local; +use crate::NamedDefinerCallback; +use crate::NamedGetterCallback; +use crate::NamedSetterCallback; use crate::Object; use crate::PropertyAttribute; use crate::PropertyEnumeratorCallback; +use crate::PropertyHandlerFlags; use crate::SideEffectType; use crate::Signature; use crate::String; use crate::Value; -use crate::NONE; use std::convert::TryFrom; use std::ffi::c_void; use std::ptr::null; @@ -110,29 +112,143 @@ extern "C" { fn v8__ObjectTemplate__SetNamedPropertyHandler( this: *const ObjectTemplate, - getter: Option, - setter: Option, - query: Option, - deleter: Option, - enumerator: Option, - descriptor: Option, + getter: Option, + setter: Option, + query: Option, + deleter: Option, + enumerator: Option, + definer: Option, + descriptor: Option, data_or_null: *const Value, + flags: PropertyHandlerFlags, ); fn v8__ObjectTemplate__SetIndexedPropertyHandler( this: *const ObjectTemplate, getter: Option, setter: Option, - query: Option, - deleter: Option, - enumerator: Option, - descriptor: Option, + query: Option, + deleter: Option, + enumerator: Option, + definer: Option, + descriptor: Option, data_or_null: *const Value, ); fn v8__ObjectTemplate__SetImmutableProto(this: *const ObjectTemplate); } +pub type AccessorNameGetterCallback<'s> = NamedGetterCallback<'s>; + +/// Note: [ReturnValue] is ignored for accessors. +pub type AccessorNameSetterCallback<'s> = NamedSetterCallback<'s>; + +/// Interceptor for get requests on an object. +/// +/// Use [ReturnValue] to set the return value of the intercepted get request. If +/// the property does not exist the callback should not set the result and must +/// not produce side effects. +/// +/// See also [ObjectTemplate::set_handler]. +pub type GenericNamedPropertyGetterCallback<'s> = NamedGetterCallback<'s>; + +/// Interceptor for set requests on an object. +/// +/// Use [ReturnValue] to indicate whether the request was intercepted or not. If +/// the setter successfully intercepts the request, i.e., if the request should +/// not be further executed, call [ReturnValue::set]. If the setter did not +/// intercept the request, i.e., if the request should be handled as if no +/// interceptor is present, do not not call set() and do not produce side +/// effects. +/// +/// See also [ObjectTemplate::set_named_property_handler]. +pub type GenericNamedPropertySetterCallback<'s> = NamedSetterCallback<'s>; + +/// Intercepts all requests that query the attributes of the property, e.g., +/// getOwnPropertyDescriptor(), propertyIsEnumerable(), and defineProperty(). +/// +/// Use [ReturnValue::set] to set the property attributes. The value is an +/// integer encoding a [PropertyAttribute]. If the property does not exist the +/// callback should not set the result and must not produce side effects. +/// +/// Note: Some functions query the property attributes internally, even though +/// they do not return the attributes. For example, hasOwnProperty() can trigger +/// this interceptor depending on the state of the object. +/// +/// See also [ObjectTemplate::set_named_property_handler]. +pub type GenericNamedPropertyQueryCallback<'s> = NamedGetterCallback<'s>; + +/// Interceptor for delete requests on an object. +/// +/// Use [ReturnValue] to indicate whether the request was intercepted or not. If +/// the deleter successfully intercepts the request, i.e., if the request should +/// not be further executed, call [ReturnValue::set] with a boolean value. The +/// value is used as the return value of delete. If the deleter does not +/// intercept the request then it should not set the result and must not produce +/// side effects. +/// +/// Note: If you need to mimic the behavior of delete, i.e., throw in strict +/// mode instead of returning false, use +/// [PropertyCallbackArguments::should_throw_on_error] to determine if you are +/// in strict mode. +/// +/// See also [ObjectTemplate::set_named_property_handler]. +pub type GenericNamedPropertyDeleterCallback<'s> = NamedGetterCallback<'s>; + +/// Returns an array containing the names of the properties the named property getter intercepts. +/// +/// Note: The values in the array must be of type v8::Name. +/// +/// See also [ObjectTemplate::set_named_property_handler]. +pub type GenericNamedPropertyEnumeratorCallback<'s> = + PropertyEnumeratorCallback<'s>; + +/// Interceptor for defineProperty requests on an object. +/// +/// Use [ReturnValue] to indicate whether the request was intercepted or not. If +/// the definer successfully intercepts the request, i.e., if the request should +/// not be further executed, call [ReturnValue::set]. If the definer did not +/// intercept the request, i.e., if the request should be handled as if no +/// interceptor is present, do not not call set() and do not produce side +/// effects. +/// +/// See also [ObjectTemplate::set_named_property_handler]. +pub type GenericNamedPropertyDefinerCallback<'s> = NamedDefinerCallback<'s>; + +/// Interceptor for getOwnPropertyDescriptor requests on an object. +/// +/// Use [ReturnValue::set] to set the return value of the intercepted request. +/// The return value must be an object that can be converted to a +/// [PropertyDescriptor], e.g., a [Value] returned from +/// `Object.getOwnPropertyDescriptor()`. +/// +/// Note: If GetOwnPropertyDescriptor is intercepted, it will always return +/// true, i.e., indicate that the property was found. +/// +/// See also [ObjectTemplate::set_named_property_handler]. +pub type GenericNamedPropertyDescriptorCallback<'s> = NamedGetterCallback<'s>; + +/// See [GenericNamedPropertyGetterCallback]. +pub type IndexedPropertyGetterCallback<'s> = IndexedGetterCallback<'s>; + +/// See [GenericNamedPropertySetterCallback]. +pub type IndexedPropertySetterCallback<'s> = IndexedSetterCallback<'s>; + +/// See [GenericNamedPropertyQueryCallback]. +pub type IndexedPropertyQueryCallback<'s> = IndexedGetterCallback<'s>; + +/// See [GenericNamedPropertyDeleterCallback]. +pub type IndexedPropertyDeleterCallback<'s> = IndexedGetterCallback<'s>; + +/// See [GenericNamedPropertyEnumeratorCallback]. +pub type IndexedPropertyEnumeratorCallback<'s> = PropertyEnumeratorCallback<'s>; + +/// See [GenericNamedPropertyDefinerCallback]. +pub type IndexedPropertyDefinerCallback<'s> = IndexedDefinerCallback<'s>; + +/// See [GenericNamedPropertyDescriptorCallback]. +pub type IndexedPropertyDescriptorCallback<'s> = IndexedGetterCallback<'s>; + pub struct AccessorConfiguration<'s> { pub(crate) getter: AccessorNameGetterCallback<'s>, pub(crate) setter: Option>, @@ -146,7 +262,7 @@ impl<'s> AccessorConfiguration<'s> { getter: getter.map_fn_to(), setter: None, data: None, - property_attribute: NONE, + property_attribute: PropertyAttribute::NONE, } } @@ -175,13 +291,15 @@ impl<'s> AccessorConfiguration<'s> { #[derive(Default)] pub struct NamedPropertyHandlerConfiguration<'s> { - pub(crate) getter: Option>, - pub(crate) setter: Option>, - pub(crate) query: Option>, - pub(crate) deleter: Option>, - pub(crate) enumerator: Option>, - pub(crate) descriptor: Option>, + pub(crate) getter: Option>, + pub(crate) setter: Option>, + pub(crate) query: Option>, + pub(crate) deleter: Option>, + pub(crate) enumerator: Option>, + pub(crate) definer: Option>, + pub(crate) descriptor: Option>, pub(crate) data: Option>, + pub(crate) flags: PropertyHandlerFlags, } impl<'s> NamedPropertyHandlerConfiguration<'s> { @@ -192,8 +310,10 @@ impl<'s> NamedPropertyHandlerConfiguration<'s> { query: None, deleter: None, enumerator: None, + definer: None, descriptor: None, data: None, + flags: PropertyHandlerFlags::NONE, } } @@ -203,12 +323,14 @@ impl<'s> NamedPropertyHandlerConfiguration<'s> { || self.query.is_some() || self.deleter.is_some() || self.enumerator.is_some() + || self.definer.is_some() || self.descriptor.is_some() + || !self.flags.is_none() } pub fn getter( mut self, - getter: impl MapFnTo>, + getter: impl MapFnTo>, ) -> Self { self.getter = Some(getter.map_fn_to()); self @@ -216,46 +338,47 @@ impl<'s> NamedPropertyHandlerConfiguration<'s> { pub fn setter( mut self, - setter: impl MapFnTo>, + setter: impl MapFnTo>, ) -> Self { self.setter = Some(setter.map_fn_to()); self } - // Intercepts all requests that query the attributes of the property, - // e.g., getOwnPropertyDescriptor(), propertyIsEnumerable(), and defineProperty() - // Use ReturnValue.set_int32(value) to set the property attributes. The value is an interger encoding a v8::PropertyAttribute. pub fn query( mut self, - query: impl MapFnTo>, + query: impl MapFnTo>, ) -> Self { self.query = Some(query.map_fn_to()); self } - // Interceptor for delete requests on an object. - // Use ReturnValue.set_bool to indicate whether the request was intercepted or not. If the deleter successfully intercepts the request, - // i.e., if the request should not be further executed, call info.GetReturnValue().Set(value) with a boolean value. - // The value is used as the return value of delete. + pub fn deleter( mut self, - deleter: impl MapFnTo>, + deleter: impl MapFnTo>, ) -> Self { self.deleter = Some(deleter.map_fn_to()); self } - // Returns an array containing the names of the properties the named property getter intercepts. - // use ReturnValue.set with a v8::Array + pub fn enumerator( mut self, - enumerator: impl MapFnTo>, + enumerator: impl MapFnTo>, ) -> Self { self.enumerator = Some(enumerator.map_fn_to()); self } + pub fn definer( + mut self, + definer: impl MapFnTo>, + ) -> Self { + self.definer = Some(definer.map_fn_to()); + self + } + pub fn descriptor( mut self, - descriptor: impl MapFnTo>, + descriptor: impl MapFnTo>, ) -> Self { self.descriptor = Some(descriptor.map_fn_to()); self @@ -266,17 +389,25 @@ impl<'s> NamedPropertyHandlerConfiguration<'s> { self.data = Some(data); self } + + /// Set the property handler flags. The default is PropertyHandlerFlags::NONE. + pub fn flags(mut self, flags: PropertyHandlerFlags) -> Self { + self.flags = flags; + self + } } #[derive(Default)] pub struct IndexedPropertyHandlerConfiguration<'s> { pub(crate) getter: Option>, pub(crate) setter: Option>, - pub(crate) query: Option>, - pub(crate) deleter: Option>, - pub(crate) enumerator: Option>, - pub(crate) descriptor: Option>, + pub(crate) query: Option>, + pub(crate) deleter: Option>, + pub(crate) enumerator: Option>, + pub(crate) definer: Option>, + pub(crate) descriptor: Option>, pub(crate) data: Option>, + pub(crate) flags: PropertyHandlerFlags, } impl<'s> IndexedPropertyHandlerConfiguration<'s> { @@ -287,8 +418,10 @@ impl<'s> IndexedPropertyHandlerConfiguration<'s> { query: None, deleter: None, enumerator: None, + definer: None, descriptor: None, data: None, + flags: PropertyHandlerFlags::NONE, } } @@ -298,7 +431,9 @@ impl<'s> IndexedPropertyHandlerConfiguration<'s> { || self.query.is_some() || self.deleter.is_some() || self.enumerator.is_some() + || self.definer.is_some() || self.descriptor.is_some() + || !self.flags.is_none() } pub fn getter( @@ -319,7 +454,7 @@ impl<'s> IndexedPropertyHandlerConfiguration<'s> { pub fn query( mut self, - query: impl MapFnTo>, + query: impl MapFnTo>, ) -> Self { self.query = Some(query.map_fn_to()); self @@ -327,7 +462,7 @@ impl<'s> IndexedPropertyHandlerConfiguration<'s> { pub fn deleter( mut self, - deleter: impl MapFnTo>, + deleter: impl MapFnTo>, ) -> Self { self.deleter = Some(deleter.map_fn_to()); self @@ -335,15 +470,23 @@ impl<'s> IndexedPropertyHandlerConfiguration<'s> { pub fn enumerator( mut self, - enumerator: impl MapFnTo>, + enumerator: impl MapFnTo>, ) -> Self { self.enumerator = Some(enumerator.map_fn_to()); self } + pub fn definer( + mut self, + definer: impl MapFnTo>, + ) -> Self { + self.definer = Some(definer.map_fn_to()); + self + } + pub fn descriptor( mut self, - descriptor: impl MapFnTo>, + descriptor: impl MapFnTo>, ) -> Self { self.descriptor = Some(descriptor.map_fn_to()); self @@ -354,13 +497,19 @@ impl<'s> IndexedPropertyHandlerConfiguration<'s> { self.data = Some(data); self } + + /// Set the property handler flags. The default is PropertyHandlerFlags::NONE. + pub fn flags(mut self, flags: PropertyHandlerFlags) -> Self { + self.flags = flags; + self + } } impl Template { /// Adds a property to each instance created by this template. #[inline(always)] pub fn set(&self, key: Local, value: Local) { - self.set_with_attr(key, value, NONE) + self.set_with_attr(key, value, PropertyAttribute::NONE) } /// Adds a property to each instance created by this template with @@ -708,8 +857,10 @@ impl ObjectTemplate { configuration.query, configuration.deleter, configuration.enumerator, + configuration.definer, configuration.descriptor, configuration.data.map_or_else(null, |p| &*p), + configuration.flags, ) } } @@ -727,6 +878,7 @@ impl ObjectTemplate { configuration.query, configuration.deleter, configuration.enumerator, + configuration.definer, configuration.descriptor, configuration.data.map_or_else(null, |p| &*p), ) diff --git a/tests/test_api.rs b/tests/test_api.rs index 7880a7af..7a437a1e 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -1507,7 +1507,9 @@ fn object_template() { let object_templ = v8::ObjectTemplate::new(scope); let function_templ = v8::FunctionTemplate::new(scope, fortytwo_callback); let name = v8::String::new(scope, "f").unwrap(); - let attr = v8::READ_ONLY | v8::DONT_ENUM | v8::DONT_DELETE; + let attr = v8::PropertyAttribute::READ_ONLY + | v8::PropertyAttribute::DONT_ENUM + | v8::PropertyAttribute::DONT_DELETE; object_templ.set_internal_field_count(1); object_templ.set_with_attr(name.into(), function_templ.into(), attr); let context = v8::Context::new(scope); @@ -1530,7 +1532,7 @@ fn object_template() { scope, name.into(), object.into(), - v8::DONT_ENUM, + v8::PropertyAttribute::DONT_ENUM, ); let source = r#" { @@ -1787,7 +1789,8 @@ fn object_template_set_accessor() { let setter = |scope: &mut v8::HandleScope, key: v8::Local, value: v8::Local, - args: v8::PropertyCallbackArguments| { + args: v8::PropertyCallbackArguments, + _rv: v8::ReturnValue| { let this = args.this(); assert_eq!(args.holder(), this); @@ -1819,24 +1822,24 @@ fn object_template_set_accessor() { rv.set(this.get_internal_field(scope, 0).unwrap()); }; - let setter_with_data = - |scope: &mut v8::HandleScope, - key: v8::Local, - value: v8::Local, - args: v8::PropertyCallbackArguments| { - let this = args.this(); + let setter_with_data = |scope: &mut v8::HandleScope, + key: v8::Local, + value: v8::Local, + args: v8::PropertyCallbackArguments, + _rv: v8::ReturnValue| { + let this = args.this(); - assert_eq!(args.holder(), this); - assert!(args.data().is_string()); - assert!(!args.should_throw_on_error()); - assert_eq!(args.data().to_rust_string_lossy(scope), "data"); + assert_eq!(args.holder(), this); + assert!(args.data().is_string()); + assert!(!args.should_throw_on_error()); + assert_eq!(args.data().to_rust_string_lossy(scope), "data"); - let expected_key = v8::String::new(scope, "key").unwrap(); - assert!(key.strict_equals(expected_key.into())); + let expected_key = v8::String::new(scope, "key").unwrap(); + assert!(key.strict_equals(expected_key.into())); - assert!(value.is_int32()); - assert!(this.set_internal_field(0, value)); - }; + assert!(value.is_int32()); + assert!(this.set_internal_field(0, value)); + }; let key = v8::String::new(scope, "key").unwrap(); let name = v8::String::new(scope, "obj").unwrap(); @@ -1972,6 +1975,11 @@ fn object_template_set_named_property_handler() { key: v8::Local, args: v8::PropertyCallbackArguments, mut rv: v8::ReturnValue| { + let fallthrough_key = v8::String::new(scope, "fallthrough").unwrap(); + if key.strict_equals(fallthrough_key.into()) { + return; + } + let this = args.this(); assert_eq!(args.holder(), this); @@ -1987,7 +1995,18 @@ fn object_template_set_named_property_handler() { let setter = |scope: &mut v8::HandleScope, key: v8::Local, value: v8::Local, - args: v8::PropertyCallbackArguments| { + args: v8::PropertyCallbackArguments, + mut rv: v8::ReturnValue| { + let fallthrough_key = v8::String::new(scope, "fallthrough").unwrap(); + if key.strict_equals(fallthrough_key.into()) { + return; + } + + let panic_on_get = v8::String::new(scope, "panicOnGet").unwrap(); + if key.strict_equals(panic_on_get.into()) { + return; + } + let this = args.this(); assert_eq!(args.holder(), this); @@ -1999,12 +2018,24 @@ fn object_template_set_named_property_handler() { assert!(value.is_int32()); assert!(this.set_internal_field(0, value)); + + rv.set_undefined(); }; let query = |scope: &mut v8::HandleScope, key: v8::Local, args: v8::PropertyCallbackArguments, mut rv: v8::ReturnValue| { + let fallthrough_key = v8::String::new(scope, "fallthrough").unwrap(); + if key.strict_equals(fallthrough_key.into()) { + return; + } + + let panic_on_get = v8::String::new(scope, "panicOnGet").unwrap(); + if key.strict_equals(panic_on_get.into()) { + return; + } + let this = args.this(); assert_eq!(args.holder(), this); @@ -2013,7 +2044,7 @@ fn object_template_set_named_property_handler() { let expected_key = v8::String::new(scope, "key").unwrap(); assert!(key.strict_equals(expected_key.into())); - //PropertyAttribute::READ_ONLY + // PropertyAttribute::READ_ONLY rv.set_int32(1); let expected_value = v8::Integer::new(scope, 42); assert!(this @@ -2021,13 +2052,23 @@ fn object_template_set_named_property_handler() { .unwrap() .strict_equals(expected_value.into())); }; + let deleter = |scope: &mut v8::HandleScope, key: v8::Local, - _args: v8::PropertyCallbackArguments, + args: v8::PropertyCallbackArguments, mut rv: v8::ReturnValue| { + let fallthrough_key = v8::String::new(scope, "fallthrough").unwrap(); + if key.strict_equals(fallthrough_key.into()) { + return; + } + + let this = args.this(); + let expected_key = v8::String::new(scope, "key").unwrap(); assert!(key.strict_equals(expected_key.into())); + assert!(this.set_internal_field(0, v8::undefined(scope).into())); + rv.set_bool(true); }; @@ -2047,11 +2088,77 @@ fn object_template_set_named_property_handler() { .unwrap() .strict_equals(expected_value.into())); - let key = v8::String::new(scope, "key").unwrap(); + let key: v8::Local = + v8::String::new(scope, "key").unwrap().into(); let result = v8::Array::new_with_elements(scope, &[key.into()]); rv.set(result.into()); }; + let definer = |scope: &mut v8::HandleScope, + key: v8::Local, + desc: &v8::PropertyDescriptor, + args: v8::PropertyCallbackArguments, + mut rv: v8::ReturnValue| { + let fallthrough_key = v8::String::new(scope, "fallthrough").unwrap(); + if key.strict_equals(fallthrough_key.into()) { + return; + } + + let this = args.this(); + + let expected_key = v8::String::new(scope, "key").unwrap(); + assert!(key.strict_equals(expected_key.into())); + + assert!(desc.has_enumerable()); + assert!(desc.has_configurable()); + assert!(desc.has_writable()); + assert!(desc.has_value()); + assert!(!desc.has_get()); + assert!(!desc.has_set()); + + assert!(desc.enumerable()); + assert!(desc.configurable()); + assert!(desc.writable()); + + let value = desc.value(); + + assert!(value.is_int32()); + assert!(this.set_internal_field(0, value)); + + rv.set_undefined(); + }; + + let descriptor = |scope: &mut v8::HandleScope, + key: v8::Local, + args: v8::PropertyCallbackArguments, + mut rv: v8::ReturnValue| { + let fallthrough_key = v8::String::new(scope, "fallthrough").unwrap(); + if key.strict_equals(fallthrough_key.into()) { + return; + } + + let this = args.this(); + + let expected_key = v8::String::new(scope, "key").unwrap(); + assert!(key.strict_equals(expected_key.into())); + + let descriptor = v8::Object::new(scope); + let value_key = v8::String::new(scope, "value").unwrap(); + let value = this.get_internal_field(scope, 0).unwrap(); + descriptor.set(scope, value_key.into(), value); + let enumerable_key = v8::String::new(scope, "enumerable").unwrap(); + let enumerable = v8::Boolean::new(scope, true); + descriptor.set(scope, enumerable_key.into(), enumerable.into()); + let configurable_key = v8::String::new(scope, "configurable").unwrap(); + let configurable = v8::Boolean::new(scope, true); + descriptor.set(scope, configurable_key.into(), configurable.into()); + let writable_key = v8::String::new(scope, "writable").unwrap(); + let writable = v8::Boolean::new(scope, true); + descriptor.set(scope, writable_key.into(), writable.into()); + + rv.set(descriptor.into()); + }; + let name = v8::String::new(scope, "obj").unwrap(); // Lone getter @@ -2070,6 +2177,14 @@ fn object_template_set_named_property_handler() { obj.into(), ); assert!(eval(scope, "obj.key").unwrap().strict_equals(int.into())); + assert!(eval(scope, "obj.fallthrough").unwrap().is_undefined()); + assert!(eval(scope, "obj.fallthrough = 'a'; obj.fallthrough") + .unwrap() + .is_string()); + assert!(obj + .get_internal_field(scope, 0) + .unwrap() + .strict_equals(int.into())); // Getter + setter + deleter let templ = v8::ObjectTemplate::new(scope); @@ -2094,8 +2209,18 @@ fn object_template_set_named_property_handler() { .get_internal_field(scope, 0) .unwrap() .strict_equals(new_int.into())); - assert!(eval(scope, "delete obj.key").unwrap().boolean_value(scope)); + assert!(obj.get_internal_field(scope, 0).unwrap().is_undefined()); + assert!(eval(scope, "delete obj.key").unwrap().boolean_value(scope)); + + assert!(eval(scope, "obj.fallthrough = 'a'; obj.fallthrough") + .unwrap() + .is_string()); + assert!(obj.get_internal_field(scope, 0).unwrap().is_undefined()); + assert!(eval(scope, "delete obj.fallthrough") + .unwrap() + .boolean_value(scope)); + assert!(eval(scope, "obj.fallthrough").unwrap().is_undefined()); // query descriptor let templ = v8::ObjectTemplate::new(scope); @@ -2111,16 +2236,16 @@ fn object_template_set_named_property_handler() { name.into(), obj.into(), ); - let result = - eval(scope, "Object.getOwnPropertyDescriptor(obj, 'key')").unwrap(); - let object = result.to_object(scope).unwrap(); - let key = v8::String::new(scope, "writable").unwrap(); - let value = object.get(scope, key.into()).unwrap(); + assert!(eval(scope, "'key' in obj").unwrap().boolean_value(scope)); + assert!(!eval(scope, "'fallthrough' in obj") + .unwrap() + .boolean_value(scope)); + eval(scope, "obj.fallthrough = 'a'").unwrap(); + assert!(eval(scope, "'fallthrough' in obj") + .unwrap() + .boolean_value(scope)); - let non_writable = v8::Boolean::new(scope, false); - assert!(value.strict_equals(non_writable.into())); - - //enumerator + // enumerator let templ = v8::ObjectTemplate::new(scope); templ.set_internal_field_count(1); templ.set_named_property_handler( @@ -2142,7 +2267,113 @@ fn object_template_set_named_property_handler() { let index = v8::Integer::new(scope, 0); let result = arr.get(scope, index.into()).unwrap(); let expected = v8::String::new(scope, "key").unwrap(); - assert!(expected.strict_equals(result)) + assert!(expected.strict_equals(result)); + eval(scope, "obj.fallthrough = 'a'").unwrap(); + let arr = v8::Local::::try_from( + eval(scope, "Object.keys(obj)").unwrap(), + ) + .unwrap(); + assert_eq!(arr.length(), 2); + + // definer + let templ = v8::ObjectTemplate::new(scope); + templ.set_internal_field_count(1); + templ.set_named_property_handler( + v8::NamedPropertyHandlerConfiguration::new().definer(definer), + ); + + let obj = templ.new_instance(scope).unwrap(); + obj.set_internal_field(0, int.into()); + scope.get_current_context().global(scope).set( + scope, + name.into(), + obj.into(), + ); + eval( + scope, + "Object.defineProperty(obj, 'key', { value: 9, enumerable: true, configurable: true, writable: true })", + ) + .unwrap(); + assert!(obj + .get_internal_field(scope, 0) + .unwrap() + .strict_equals(new_int.into())); + assert!(eval( + scope, + "Object.defineProperty(obj, 'fallthrough', { value: 'a' }); obj.fallthrough" + ) + .unwrap() + .is_string()); + + // descriptor + let templ = v8::ObjectTemplate::new(scope); + templ.set_internal_field_count(1); + templ.set_named_property_handler( + v8::NamedPropertyHandlerConfiguration::new().descriptor(descriptor), + ); + + let obj = templ.new_instance(scope).unwrap(); + obj.set_internal_field(0, int.into()); + scope.get_current_context().global(scope).set( + scope, + name.into(), + obj.into(), + ); + let desc = eval(scope, "Object.getOwnPropertyDescriptor(obj, 'key')") + .unwrap() + .to_object(scope) + .unwrap(); + let expected_value = v8::Integer::new(scope, 42); + let value_key = v8::String::new(scope, "value").unwrap().into(); + assert!(desc + .get(scope, value_key) + .unwrap() + .strict_equals(expected_value.into())); + let enumerable_key = v8::String::new(scope, "enumerable").unwrap().into(); + assert!(desc + .get(scope, enumerable_key) + .unwrap() + .boolean_value(scope)); + let configurable_key = + v8::String::new(scope, "configurable").unwrap().into(); + assert!(desc + .get(scope, configurable_key) + .unwrap() + .boolean_value(scope)); + let writable_key = v8::String::new(scope, "writable").unwrap().into(); + assert!(desc.get(scope, writable_key).unwrap().boolean_value(scope)); + assert!( + eval(scope, "Object.getOwnPropertyDescriptor(obj, 'fallthrough')") + .unwrap() + .is_undefined() + ); + + // Getter + Setter + Query + NON_MASKING + let templ = v8::ObjectTemplate::new(scope); + templ.set_internal_field_count(1); + templ.set_named_property_handler( + v8::NamedPropertyHandlerConfiguration::new() + .getter(getter) + .setter(setter) + .query(query) + .flags(v8::PropertyHandlerFlags::NON_MASKING), + ); + + let obj = templ.new_instance(scope).unwrap(); + obj.set_internal_field(0, int.into()); + scope.get_current_context().global(scope).set( + scope, + name.into(), + obj.into(), + ); + assert!(!eval(scope, "'panicOnGet' in obj") + .unwrap() + .boolean_value(scope)); + eval(scope, "obj.panicOnGet = 'x'").unwrap(); + assert!(eval(scope, "'panicOnGet' in obj") + .unwrap() + .boolean_value(scope)); + assert!(eval(scope, "obj.panicOnGet").unwrap().is_string()); } } @@ -2172,7 +2403,8 @@ fn object_template_set_indexed_property_handler() { let setter = |_scope: &mut v8::HandleScope, index: u32, value: v8::Local, - args: v8::PropertyCallbackArguments| { + args: v8::PropertyCallbackArguments, + mut rv: v8::ReturnValue| { let this = args.this(); assert_eq!(args.holder(), this); @@ -2183,6 +2415,22 @@ fn object_template_set_indexed_property_handler() { assert!(value.is_int32()); assert!(this.set_internal_field(0, value)); + + rv.set_undefined(); + }; + + let query = |_scope: &mut v8::HandleScope, + index: u32, + _args: v8::PropertyCallbackArguments, + mut rv: v8::ReturnValue| { + if index == 12 { + return; + } + + assert_eq!(index, 37); + + // PropertyAttribute::READ_ONLY + rv.set_int32(1); }; let deleter = |_scope: &mut v8::HandleScope, @@ -2215,6 +2463,53 @@ fn object_template_set_indexed_property_handler() { rv.set(result.into()); }; + let definer = |_scope: &mut v8::HandleScope, + index: u32, + desc: &v8::PropertyDescriptor, + args: v8::PropertyCallbackArguments, + mut rv: v8::ReturnValue| { + let this = args.this(); + + assert_eq!(index, 37); + + assert!(!desc.has_enumerable()); + assert!(!desc.has_configurable()); + assert!(!desc.has_writable()); + assert!(desc.has_value()); + assert!(!desc.has_get()); + assert!(!desc.has_set()); + + let value = desc.value(); + this.set_internal_field(0, value); + + rv.set_undefined(); + }; + + let descriptor = |scope: &mut v8::HandleScope, + index: u32, + args: v8::PropertyCallbackArguments, + mut rv: v8::ReturnValue| { + let this = args.this(); + + assert_eq!(index, 37); + + let descriptor = v8::Object::new(scope); + let value_key = v8::String::new(scope, "value").unwrap(); + let value = this.get_internal_field(scope, 0).unwrap(); + descriptor.set(scope, value_key.into(), value); + let enumerable_key = v8::String::new(scope, "enumerable").unwrap(); + let enumerable = v8::Boolean::new(scope, true); + descriptor.set(scope, enumerable_key.into(), enumerable.into()); + let configurable_key = v8::String::new(scope, "configurable").unwrap(); + let configurable = v8::Boolean::new(scope, true); + descriptor.set(scope, configurable_key.into(), configurable.into()); + let writable_key = v8::String::new(scope, "writable").unwrap(); + let writable = v8::Boolean::new(scope, true); + descriptor.set(scope, writable_key.into(), writable.into()); + + rv.set(descriptor.into()); + }; + let name = v8::String::new(scope, "obj").unwrap(); // Lone getter @@ -2258,7 +2553,22 @@ fn object_template_set_indexed_property_handler() { assert!(!eval(scope, "delete obj[37]").unwrap().boolean_value(scope)); - //Enumerator + // Query + let templ = v8::ObjectTemplate::new(scope); + templ.set_internal_field_count(1); + templ.set_indexed_property_handler( + v8::IndexedPropertyHandlerConfiguration::new().query(query), + ); + + let obj = templ.new_instance(scope).unwrap(); + obj.set_internal_field(0, int.into()); + scope + .get_current_context() + .global(scope) + .set(scope, name.into(), obj.into()); + assert!(eval(scope, "'37' in obj").unwrap().boolean_value(scope)); + + // Enumerator let templ = v8::ObjectTemplate::new(scope); templ.set_internal_field_count(1); templ.set_indexed_property_handler( @@ -2285,8 +2595,61 @@ fn object_template_set_indexed_property_handler() { ", ) .unwrap(); - assert!(value.strict_equals(int.into())); + + // Definer + let templ = v8::ObjectTemplate::new(scope); + templ.set_internal_field_count(1); + templ.set_indexed_property_handler( + v8::IndexedPropertyHandlerConfiguration::new().definer(definer), + ); + + let obj = templ.new_instance(scope).unwrap(); + obj.set_internal_field(0, int.into()); + scope + .get_current_context() + .global(scope) + .set(scope, name.into(), obj.into()); + eval(scope, "Object.defineProperty(obj, 37, { value: 9 })").unwrap(); + assert!(obj + .get_internal_field(scope, 0) + .unwrap() + .strict_equals(new_int.into())); + + // Descriptor + let templ = v8::ObjectTemplate::new(scope); + templ.set_internal_field_count(1); + templ.set_indexed_property_handler( + v8::IndexedPropertyHandlerConfiguration::new().descriptor(descriptor), + ); + + let obj = templ.new_instance(scope).unwrap(); + obj.set_internal_field(0, int.into()); + scope + .get_current_context() + .global(scope) + .set(scope, name.into(), obj.into()); + let desc = eval(scope, "Object.getOwnPropertyDescriptor(obj, 37)") + .unwrap() + .to_object(scope) + .unwrap(); + let value_key = v8::String::new(scope, "value").unwrap().into(); + assert!(desc + .get(scope, value_key) + .unwrap() + .strict_equals(int.into())); + let enumerable_key = v8::String::new(scope, "enumerable").unwrap().into(); + assert!(desc + .get(scope, enumerable_key) + .unwrap() + .boolean_value(scope)); + let configurable_key = v8::String::new(scope, "configurable").unwrap().into(); + assert!(desc + .get(scope, configurable_key) + .unwrap() + .boolean_value(scope)); + let writable_key = v8::String::new(scope, "writable").unwrap().into(); + assert!(desc.get(scope, writable_key).unwrap().boolean_value(scope)); } #[test] @@ -2624,7 +2987,8 @@ fn object_set_accessor_with_setter() { let setter = |scope: &mut v8::HandleScope, key: v8::Local, value: v8::Local, - args: v8::PropertyCallbackArguments| { + args: v8::PropertyCallbackArguments, + _rv: v8::ReturnValue| { println!("setter called"); let this = args.this(); @@ -2725,7 +3089,8 @@ fn object_set_accessor_with_setter_with_property() { let setter = |scope: &mut v8::HandleScope, key: v8::Local, value: v8::Local, - args: v8::PropertyCallbackArguments| { + args: v8::PropertyCallbackArguments, + _rv: v8::ReturnValue| { println!("setter called"); let this = args.this(); @@ -2757,7 +3122,7 @@ fn object_set_accessor_with_setter_with_property() { getter_setter_key.into(), AccessorConfiguration::new(getter) .setter(setter) - .property_attribute(v8::READ_ONLY), + .property_attribute(v8::PropertyAttribute::READ_ONLY), ); let int_key = v8::String::new(scope, "int_key").unwrap(); @@ -2830,7 +3195,8 @@ fn object_set_accessor_with_data() { let setter = |scope: &mut v8::HandleScope, key: v8::Local, value: v8::Local, - args: v8::PropertyCallbackArguments| { + args: v8::PropertyCallbackArguments, + _rv: v8::ReturnValue| { println!("setter called"); let this = args.this(); @@ -6363,7 +6729,8 @@ fn test_object_get_property_names() { scope, v8::GetPropertyNamesArgs { mode: v8::KeyCollectionMode::IncludePrototypes, - property_filter: v8::ONLY_ENUMERABLE | v8::SKIP_SYMBOLS, + property_filter: v8::PropertyFilter::ONLY_ENUMERABLE + | v8::PropertyFilter::SKIP_SYMBOLS, index_filter: v8::IndexFilter::IncludeIndices, key_conversion: v8::KeyConversionMode::KeepNumbers, }, @@ -9523,7 +9890,7 @@ fn gc_callbacks() { data: *mut c_void, ) { // We should get a mark-sweep GC here. - assert_eq!(r#type, v8::GC_TYPE_MARK_SWEEP_COMPACT); + assert_eq!(r#type, v8::GCType::MARK_SWEEP_COMPACT); let state = unsafe { &mut *(data as *mut GCCallbackState) }; state.mark_sweep_calls += 1; } @@ -9535,7 +9902,7 @@ fn gc_callbacks() { data: *mut c_void, ) { // We should get a mark-sweep GC here. - assert_eq!(r#type, v8::GC_TYPE_INCREMENTAL_MARKING); + assert_eq!(r#type, v8::GCType::INCREMENTAL_MARKING); let state = unsafe { &mut *(data as *mut GCCallbackState) }; state.incremental_marking_calls += 1; } @@ -9543,11 +9910,11 @@ fn gc_callbacks() { let mut state = GCCallbackState::default(); let state_ptr = &mut state as *mut _ as *mut c_void; let isolate = &mut v8::Isolate::new(Default::default()); - isolate.add_gc_prologue_callback(callback, state_ptr, v8::GC_TYPE_ALL); + isolate.add_gc_prologue_callback(callback, state_ptr, v8::GCType::ALL); isolate.add_gc_prologue_callback( callback2, state_ptr, - v8::GC_TYPE_INCREMENTAL_MARKING | v8::GC_TYPE_PROCESS_WEAK_CALLBACK, + v8::GCType::INCREMENTAL_MARKING | v8::GCType::PROCESS_WEAK_CALLBACKS, ); {