0
0
Fork 0
mirror of https://github.com/denoland/rusty_v8.git synced 2025-01-11 08:34:01 -05:00

feat: Add ObjectTemplate NamedProperty and IndexedProperty handlers (#1064)

This commit is contained in:
Giovanny Gutiérrez 2022-10-05 17:23:01 -05:00 committed by GitHub
parent 760c48a089
commit cd51ea1bf2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 635 additions and 0 deletions

View file

@ -1135,6 +1135,33 @@ void v8__ObjectTemplate__SetAccessorWithSetter(
ptr_to_local(&self)->SetAccessor(ptr_to_local(&key), getter, setter);
}
void v8__ObjectTemplate__SetNamedPropertyHandler(
const v8::ObjectTemplate& self,
v8::GenericNamedPropertyGetterCallback getter,
v8::GenericNamedPropertySetterCallback setter,
v8::GenericNamedPropertyQueryCallback query,
v8::GenericNamedPropertyDeleterCallback deleter,
v8::GenericNamedPropertyEnumeratorCallback enumerator,
v8::GenericNamedPropertyDescriptorCallback descriptor,
const v8::Value* data_or_null) {
ptr_to_local(&self)->SetHandler(v8::NamedPropertyHandlerConfiguration(
getter, setter, query, deleter, enumerator, nullptr, descriptor,
ptr_to_local(data_or_null)));
}
void v8__ObjectTemplate__SetIndexedPropertyHandler(
const v8::ObjectTemplate& self, v8::IndexedPropertyGetterCallback getter,
v8::IndexedPropertySetterCallback setter,
v8::IndexedPropertyQueryCallback query,
v8::IndexedPropertyDeleterCallback deleter,
v8::IndexedPropertyEnumeratorCallback enumerator,
v8::IndexedPropertyDescriptorCallback descriptor,
const v8::Value* data_or_null) {
ptr_to_local(&self)->SetHandler(v8::IndexedPropertyHandlerConfiguration(
getter, setter, query, deleter, enumerator, nullptr, descriptor,
ptr_to_local(data_or_null)));
}
void v8__ObjectTemplate__SetAccessorProperty(const v8::ObjectTemplate& self,
const v8::Name& key,
v8::FunctionTemplate& getter,

View file

@ -400,6 +400,66 @@ where
}
}
//Should return an Array in Return Value
pub type PropertyEnumeratorCallback<'s> =
extern "C" fn(*const PropertyCallbackInfo);
impl<F> MapFnFrom<F> for PropertyEnumeratorCallback<'_>
where
F: UnitType + Fn(&mut HandleScope, PropertyCallbackArguments, ReturnValue),
{
fn mapping() -> Self {
let f = |info: *const PropertyCallbackInfo| {
let info = info as *const PropertyCallbackInfo;
let scope = &mut unsafe { CallbackScope::new(&*info) };
let args = PropertyCallbackArguments::from_property_callback_info(info);
let rv = ReturnValue::from_property_callback_info(info);
(F::get())(scope, args, rv);
};
f.to_c_fn()
}
}
/// 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);
impl<F> MapFnFrom<F> for IndexedPropertyGetterCallback<'_>
where
F: UnitType
+ Fn(&mut HandleScope, u32, PropertyCallbackArguments, ReturnValue),
{
fn mapping() -> Self {
let f = |index: u32, info: *const PropertyCallbackInfo| {
let scope = &mut unsafe { CallbackScope::new(&*info) };
let args = PropertyCallbackArguments::from_property_callback_info(info);
let rv = ReturnValue::from_property_callback_info(info);
(F::get())(scope, index, args, rv);
};
f.to_c_fn()
}
}
pub type IndexedPropertySetterCallback<'s> =
extern "C" fn(u32, Local<'s, Value>, *const PropertyCallbackInfo);
impl<F> MapFnFrom<F> for IndexedPropertySetterCallback<'_>
where
F: UnitType
+ Fn(&mut HandleScope, u32, Local<Value>, PropertyCallbackArguments),
{
fn mapping() -> Self {
let f =
|index: u32, value: Local<Value>, info: *const PropertyCallbackInfo| {
let scope = &mut unsafe { CallbackScope::new(&*info) };
let args = PropertyCallbackArguments::from_property_callback_info(info);
(F::get())(scope, index, value, args);
};
f.to_c_fn()
}
}
/// A builder to construct the properties of a Function or FunctionTemplate.
pub struct FunctionBuilder<'s, T> {
pub(crate) callback: FunctionCallback,

View file

@ -19,9 +19,12 @@ use crate::Function;
use crate::FunctionBuilder;
use crate::FunctionCallback;
use crate::HandleScope;
use crate::IndexedPropertyGetterCallback;
use crate::IndexedPropertySetterCallback;
use crate::Local;
use crate::Object;
use crate::PropertyAttribute;
use crate::PropertyEnumeratorCallback;
use crate::SideEffectType;
use crate::Signature;
use crate::String;
@ -107,9 +110,215 @@ extern "C" {
setter: *const FunctionTemplate,
attr: PropertyAttribute,
);
fn v8__ObjectTemplate__SetNamedPropertyHandler(
this: *const ObjectTemplate,
getter: Option<AccessorNameGetterCallback>,
setter: Option<AccessorNameSetterCallback>,
query: Option<AccessorNameGetterCallback>,
deleter: Option<AccessorNameGetterCallback>,
enumerator: Option<PropertyEnumeratorCallback>,
descriptor: Option<AccessorNameGetterCallback>,
data_or_null: *const Value,
);
fn v8__ObjectTemplate__SetIndexedPropertyHandler(
this: *const ObjectTemplate,
getter: Option<IndexedPropertyGetterCallback>,
setter: Option<IndexedPropertySetterCallback>,
query: Option<IndexedPropertyGetterCallback>,
deleter: Option<IndexedPropertyGetterCallback>,
enumerator: Option<PropertyEnumeratorCallback>,
descriptor: Option<IndexedPropertyGetterCallback>,
data_or_null: *const Value,
);
fn v8__ObjectTemplate__SetImmutableProto(this: *const ObjectTemplate);
}
#[derive(Default)]
pub struct NamedPropertyHandlerConfiguration<'s> {
pub(crate) getter: Option<AccessorNameGetterCallback<'s>>,
pub(crate) setter: Option<AccessorNameSetterCallback<'s>>,
pub(crate) query: Option<AccessorNameGetterCallback<'s>>,
pub(crate) deleter: Option<AccessorNameGetterCallback<'s>>,
pub(crate) enumerator: Option<PropertyEnumeratorCallback<'s>>,
pub(crate) descriptor: Option<AccessorNameGetterCallback<'s>>,
pub(crate) data: Option<Local<'s, Value>>,
}
impl<'s> NamedPropertyHandlerConfiguration<'s> {
pub fn new() -> Self {
Self {
getter: None,
setter: None,
query: None,
deleter: None,
enumerator: None,
descriptor: None,
data: None,
}
}
pub fn is_some(&self) -> bool {
self.getter.is_some()
|| self.setter.is_some()
|| self.query.is_some()
|| self.deleter.is_some()
|| self.enumerator.is_some()
|| self.descriptor.is_some()
}
pub fn getter(
mut self,
getter: impl MapFnTo<AccessorNameGetterCallback<'s>>,
) -> Self {
self.getter = Some(getter.map_fn_to());
self
}
pub fn setter(
mut self,
setter: impl MapFnTo<AccessorNameSetterCallback<'s>>,
) -> 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<AccessorNameGetterCallback<'s>>,
) -> 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<AccessorNameGetterCallback<'s>>,
) -> 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<PropertyEnumeratorCallback<'s>>,
) -> Self {
self.enumerator = Some(enumerator.map_fn_to());
self
}
pub fn descriptor(
mut self,
descriptor: impl MapFnTo<AccessorNameGetterCallback<'s>>,
) -> Self {
self.descriptor = Some(descriptor.map_fn_to());
self
}
/// Set the associated data. The default is no associated data.
pub fn data(mut self, data: Local<'s, Value>) -> Self {
self.data = Some(data);
self
}
}
#[derive(Default)]
pub struct IndexedPropertyHandlerConfiguration<'s> {
pub(crate) getter: Option<IndexedPropertyGetterCallback<'s>>,
pub(crate) setter: Option<IndexedPropertySetterCallback<'s>>,
pub(crate) query: Option<IndexedPropertyGetterCallback<'s>>,
pub(crate) deleter: Option<IndexedPropertyGetterCallback<'s>>,
pub(crate) enumerator: Option<PropertyEnumeratorCallback<'s>>,
pub(crate) descriptor: Option<IndexedPropertyGetterCallback<'s>>,
pub(crate) data: Option<Local<'s, Value>>,
}
impl<'s> IndexedPropertyHandlerConfiguration<'s> {
pub fn new() -> Self {
Self {
getter: None,
setter: None,
query: None,
deleter: None,
enumerator: None,
descriptor: None,
data: None,
}
}
pub fn is_some(&self) -> bool {
self.getter.is_some()
|| self.setter.is_some()
|| self.query.is_some()
|| self.deleter.is_some()
|| self.enumerator.is_some()
|| self.descriptor.is_some()
}
pub fn getter(
mut self,
getter: impl MapFnTo<IndexedPropertyGetterCallback<'s>>,
) -> Self {
self.getter = Some(getter.map_fn_to());
self
}
pub fn setter(
mut self,
setter: impl MapFnTo<IndexedPropertySetterCallback<'s>>,
) -> Self {
self.setter = Some(setter.map_fn_to());
self
}
pub fn query(
mut self,
query: impl MapFnTo<IndexedPropertyGetterCallback<'s>>,
) -> Self {
self.query = Some(query.map_fn_to());
self
}
pub fn deleter(
mut self,
deleter: impl MapFnTo<IndexedPropertyGetterCallback<'s>>,
) -> Self {
self.deleter = Some(deleter.map_fn_to());
self
}
pub fn enumerator(
mut self,
enumerator: impl MapFnTo<PropertyEnumeratorCallback<'s>>,
) -> Self {
self.enumerator = Some(enumerator.map_fn_to());
self
}
pub fn descriptor(
mut self,
descriptor: impl MapFnTo<IndexedPropertyGetterCallback<'s>>,
) -> Self {
self.descriptor = Some(descriptor.map_fn_to());
self
}
/// Set the associated data. The default is no associated data.
pub fn data(mut self, data: Local<'s, Value>) -> Self {
self.data = Some(data);
self
}
}
impl Template {
/// Adds a property to each instance created by this template.
#[inline(always)]
@ -413,6 +622,46 @@ impl ObjectTemplate {
}
}
//Re uses the AccessorNameGetterCallback to avoid implementation conflicts since the declaration for
//GenericNamedPropertyGetterCallback and AccessorNameGetterCallback are the same
pub fn set_named_property_handler(
&self,
configuration: NamedPropertyHandlerConfiguration,
) {
assert!(configuration.is_some());
unsafe {
v8__ObjectTemplate__SetNamedPropertyHandler(
self,
configuration.getter,
configuration.setter,
configuration.query,
configuration.deleter,
configuration.enumerator,
configuration.descriptor,
configuration.data.map_or_else(null, |p| &*p),
)
}
}
pub fn set_indexed_property_handler(
&self,
configuration: IndexedPropertyHandlerConfiguration,
) {
assert!(configuration.is_some());
unsafe {
v8__ObjectTemplate__SetIndexedPropertyHandler(
self,
configuration.getter,
configuration.setter,
configuration.query,
configuration.deleter,
configuration.enumerator,
configuration.descriptor,
configuration.data.map_or_else(null, |p| &*p),
)
}
}
/// Sets an [accessor property](https://tc39.es/ecma262/#sec-property-attributes)
/// on the object template.
///

View file

@ -1762,6 +1762,305 @@ fn object_template_set_accessor() {
}
}
#[test]
fn object_template_set_named_property_handler() {
let _setup_guard = setup();
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 getter = |scope: &mut v8::HandleScope,
key: v8::Local<v8::Name>,
args: v8::PropertyCallbackArguments,
mut rv: v8::ReturnValue| {
let this = args.this();
let expected_key = v8::String::new(scope, "key").unwrap();
assert!(key.strict_equals(expected_key.into()));
rv.set(this.get_internal_field(scope, 0).unwrap());
};
let setter = |scope: &mut v8::HandleScope,
key: v8::Local<v8::Name>,
value: v8::Local<v8::Value>,
args: v8::PropertyCallbackArguments| {
let this = args.this();
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));
};
let query = |scope: &mut v8::HandleScope,
key: v8::Local<v8::Name>,
args: v8::PropertyCallbackArguments,
mut rv: v8::ReturnValue| {
let this = args.this();
let expected_key = v8::String::new(scope, "key").unwrap();
assert!(key.strict_equals(expected_key.into()));
//PropertyAttribute::READ_ONLY
rv.set_int32(1);
let expected_value = v8::Integer::new(scope, 42);
assert!(this
.get_internal_field(scope, 0)
.unwrap()
.strict_equals(expected_value.into()));
};
let deleter = |scope: &mut v8::HandleScope,
key: v8::Local<v8::Name>,
_args: v8::PropertyCallbackArguments,
mut rv: v8::ReturnValue| {
let expected_key = v8::String::new(scope, "key").unwrap();
assert!(key.strict_equals(expected_key.into()));
rv.set_bool(true);
};
let enumerator = |scope: &mut v8::HandleScope,
args: v8::PropertyCallbackArguments,
mut rv: v8::ReturnValue| {
let this = args.this();
//Validate is the current object
let expected_value = v8::Integer::new(scope, 42);
assert!(this
.get_internal_field(scope, 0)
.unwrap()
.strict_equals(expected_value.into()));
let key = v8::String::new(scope, "key").unwrap();
let result = v8::Array::new_with_elements(scope, &[key.into()]);
rv.set(result.into());
};
let name = v8::String::new(scope, "obj").unwrap();
// Lone getter
let templ = v8::ObjectTemplate::new(scope);
templ.set_internal_field_count(1);
templ.set_named_property_handler(
v8::NamedPropertyHandlerConfiguration::new().getter(getter),
);
let obj = templ.new_instance(scope).unwrap();
let int = v8::Integer::new(scope, 42);
obj.set_internal_field(0, int.into());
scope.get_current_context().global(scope).set(
scope,
name.into(),
obj.into(),
);
assert!(eval(scope, "obj.key").unwrap().strict_equals(int.into()));
// Getter + setter + deleter
let templ = v8::ObjectTemplate::new(scope);
templ.set_internal_field_count(1);
templ.set_named_property_handler(
v8::NamedPropertyHandlerConfiguration::new()
.getter(getter)
.setter(setter)
.deleter(deleter),
);
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 new_int = v8::Integer::new(scope, 9);
eval(scope, "obj.key = 9");
assert!(obj
.get_internal_field(scope, 0)
.unwrap()
.strict_equals(new_int.into()));
assert!(eval(scope, "delete obj.key").unwrap().boolean_value(scope));
// query descriptor
let templ = v8::ObjectTemplate::new(scope);
templ.set_internal_field_count(1);
templ.set_named_property_handler(
v8::NamedPropertyHandlerConfiguration::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(),
);
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();
let non_writable = v8::Boolean::new(scope, false);
assert!(value.strict_equals(non_writable.into()));
//enumerator
let templ = v8::ObjectTemplate::new(scope);
templ.set_internal_field_count(1);
templ.set_named_property_handler(
v8::NamedPropertyHandlerConfiguration::new().enumerator(enumerator),
);
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 arr = v8::Local::<v8::Array>::try_from(
eval(scope, "Object.keys(obj)").unwrap(),
)
.unwrap();
assert_eq!(arr.length(), 1);
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))
}
}
#[test]
fn object_template_set_indexed_property_handler() {
let _setup_guard = setup();
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 getter = |scope: &mut v8::HandleScope,
index: u32,
args: v8::PropertyCallbackArguments,
mut rv: v8::ReturnValue| {
let this = args.this();
let expected_index = 37;
assert!(index.eq(&expected_index));
rv.set(this.get_internal_field(scope, 0).unwrap());
};
let setter = |_scope: &mut v8::HandleScope,
index: u32,
value: v8::Local<v8::Value>,
args: v8::PropertyCallbackArguments| {
let this = args.this();
assert_eq!(index, 37);
assert!(value.is_int32());
assert!(this.set_internal_field(0, value));
};
let deleter = |_scope: &mut v8::HandleScope,
index: u32,
_args: v8::PropertyCallbackArguments,
mut rv: v8::ReturnValue| {
assert_eq!(index, 37);
rv.set_bool(false);
};
let enumerator = |scope: &mut v8::HandleScope,
args: v8::PropertyCallbackArguments,
mut rv: v8::ReturnValue| {
let this = args.this();
//Validate is the current object
let expected_value = v8::Integer::new(scope, 42);
assert!(this
.get_internal_field(scope, 0)
.unwrap()
.strict_equals(expected_value.into()));
let key = v8::Integer::new(scope, 37);
let result = v8::Array::new_with_elements(scope, &[key.into()]);
rv.set(result.into());
};
let name = v8::String::new(scope, "obj").unwrap();
// Lone getter
let templ = v8::ObjectTemplate::new(scope);
templ.set_internal_field_count(1);
templ.set_indexed_property_handler(
v8::IndexedPropertyHandlerConfiguration::new().getter(getter),
);
let obj = templ.new_instance(scope).unwrap();
let int = v8::Integer::new(scope, 42);
obj.set_internal_field(0, int.into());
scope
.get_current_context()
.global(scope)
.set(scope, name.into(), obj.into());
assert!(eval(scope, "obj[37]").unwrap().strict_equals(int.into()));
// Getter + setter + deleter
let templ = v8::ObjectTemplate::new(scope);
templ.set_internal_field_count(1);
templ.set_indexed_property_handler(
v8::IndexedPropertyHandlerConfiguration::new()
.getter(getter)
.setter(setter)
.deleter(deleter),
);
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 new_int = v8::Integer::new(scope, 9);
eval(scope, "obj[37] = 9");
assert!(obj
.get_internal_field(scope, 0)
.unwrap()
.strict_equals(new_int.into()));
assert!(!eval(scope, "delete obj[37]").unwrap().boolean_value(scope));
//Enumerator
let templ = v8::ObjectTemplate::new(scope);
templ.set_internal_field_count(1);
templ.set_indexed_property_handler(
v8::IndexedPropertyHandlerConfiguration::new()
.getter(getter)
.enumerator(enumerator),
);
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 value = eval(
scope,
"
let value = -1;
for (const i in obj) {
value = obj[i];
}
value
",
)
.unwrap();
assert!(value.strict_equals(int.into()));
}
#[test]
fn object() {
let _setup_guard = setup();