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:
parent
760c48a089
commit
cd51ea1bf2
4 changed files with 635 additions and 0 deletions
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
249
src/template.rs
249
src/template.rs
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue