diff --git a/src/binding.cc b/src/binding.cc index 6fd0457d..bc1dc7f3 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -599,6 +599,13 @@ MaybeBool v8__Object__CreateDataProperty(v8::Object& self, return maybe_to_maybe_bool(self.CreateDataProperty(context, key, value)); } +MaybeBool v8__Object__SetAccessor(v8::Object& self, + v8::Local context, + v8::Local key, + v8::AccessorNameGetterCallback getter) { + return maybe_to_maybe_bool(self.SetAccessor(context, key, getter)); +} + v8::Isolate* v8__Object__GetIsolate(v8::Object& self) { return self.GetIsolate(); } diff --git a/src/lib.rs b/src/lib.rs index 49fe726b..cabbce18 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,6 +94,7 @@ pub use isolate::*; pub use local::Local; pub use locker::Locker; pub use module::*; +pub use object::*; pub use primitive_array::PrimitiveArray; pub use primitives::*; pub use promise::{PromiseRejectEvent, PromiseRejectMessage, PromiseState}; diff --git a/src/object.rs b/src/object.rs index cc8ea38e..f342bdc0 100644 --- a/src/object.rs +++ b/src/object.rs @@ -6,9 +6,13 @@ use crate::Context; use crate::Local; use crate::Name; use crate::Object; +use crate::PropertyCallbackInfo; use crate::ToLocal; use crate::Value; +pub type AccessorNameGetterCallback = + extern "C" fn(Local, &PropertyCallbackInfo); + extern "C" { fn v8__Object__New(isolate: *mut Isolate) -> *mut Object; fn v8__Object__New2( @@ -18,6 +22,12 @@ extern "C" { values: *mut *mut Value, length: usize, ) -> *mut Object; + fn v8__Object__SetAccessor( + self_: &Object, + context: *const Context, + name: *const Name, + getter: AccessorNameGetterCallback, + ) -> MaybeBool; fn v8__Object__GetIsolate(object: &Object) -> &mut Isolate; fn v8__Object__Get( @@ -124,6 +134,16 @@ impl Object { } } + /// Note: SideEffectType affects the getter only, not the setter. + pub fn set_accessor( + &mut self, + context: Local, + name: Local, + getter: AccessorNameGetterCallback, + ) -> MaybeBool { + unsafe { v8__Object__SetAccessor(self, &*context, &*name, getter) } + } + /// Return the isolate to which the Object belongs to. pub fn get_isolate(&mut self) -> &Isolate { unsafe { v8__Object__GetIsolate(self) } diff --git a/src/property.rs b/src/property.rs index 5a93ca51..adddfb2f 100644 --- a/src/property.rs +++ b/src/property.rs @@ -1,9 +1,9 @@ use crate::isolate::Isolate; use crate::support::Opaque; +use crate::InIsolate; use crate::Local; use crate::Object; use crate::ReturnValue; - use std::mem::MaybeUninit; /// The information passed to a property callback about the context @@ -50,3 +50,9 @@ impl PropertyCallbackInfo { unsafe { Local::from_raw(v8__PropertyCallbackInfo__This(self)).unwrap() } } } + +impl InIsolate for PropertyCallbackInfo { + fn isolate(&mut self) -> &mut Isolate { + self.get_isolate() + } +} diff --git a/tests/test_api.rs b/tests/test_api.rs index e2f05eac..e476af8f 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -909,6 +909,55 @@ fn create_data_property() { drop(locker); } +#[test] +fn object_set_accessor() { + setup(); + let mut params = v8::Isolate::create_params(); + params.set_array_buffer_allocator(v8::new_default_allocator()); + let isolate = v8::Isolate::new(params); + let mut locker = v8::Locker::new(&isolate); + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); + let mut context = v8::Context::new(scope); + context.enter(); + let mut obj = v8::Object::new(scope); + let key = v8_str(scope, "key"); + static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); + extern "C" fn getter( + name: v8::Local, + info: &v8::PropertyCallbackInfo, + ) { + let rv = &mut info.get_return_value(); + // TODO fix callback mutability. + #[allow(mutable_transmutes)] + #[allow(clippy::transmute_ptr_to_ptr)] + let info: &mut v8::PropertyCallbackInfo = + unsafe { std::mem::transmute(info) }; + { + let mut hs = v8::HandleScope::new(info); + let scope = hs.enter(); + let name_str = + name.to_string(scope).unwrap().to_rust_string_lossy(scope); + assert_eq!(name_str, "key"); + let s = v8::String::new(scope, "hello").unwrap(); + rv.set(s.into()); + } + CALL_COUNT.fetch_add(1, Ordering::SeqCst); + } + obj.set_accessor(context, key.into(), getter); + let global = context.global(scope); + let obj_name = v8_str(scope, "obj"); + global.set(context, obj_name.into(), obj.into()); + let actual = eval(scope, context, "obj.key;").unwrap(); + let expected = v8_str(scope, "hello"); + assert!(actual.strict_equals(expected.into())); + assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 1); + context.exit(); + } + drop(locker); +} + #[test] fn promise_resolved() { setup();