From 7b0269b4472641aeb82ac36480c81e215129b5e2 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 19 Jan 2020 00:38:41 +0100 Subject: [PATCH] Add ObjectTemplate and Template::set() (#225) The ObjectTemplate type already existed but now it can also be instantiated. This commit also adds Template::set() to actually make that useful. --- src/binding.cc | 18 +++++++++ src/function.rs | 36 ------------------ src/lib.rs | 2 + src/template.rs | 96 +++++++++++++++++++++++++++++++++++++++++++++++ tests/test_api.rs | 65 ++++++++++++++++++++++++++++++++ 5 files changed, 181 insertions(+), 36 deletions(-) create mode 100644 src/template.rs diff --git a/src/binding.cc b/src/binding.cc index 70d437a4..749b2da9 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -625,6 +625,24 @@ int v8__String__WriteUtf8(const v8::String& self, v8::Isolate* isolate, return self.WriteUtf8(isolate, buffer, length, nchars_ref, options); } +void v8__Template__Set(v8::Template& self, v8::Local key, + v8::Local value) { + // TODO(bnoordhuis) Allow setting PropertyAttributes. + self.Set(key, value); +} + +v8::ObjectTemplate* v8__ObjectTemplate__New( + v8::Isolate* isolate, + v8::Local templ) { + return local_to_ptr(v8::ObjectTemplate::New(isolate, templ)); +} + +v8::Object* v8__ObjectTemplate__NewInstance( + v8::ObjectTemplate& self, + v8::Local context) { + return maybe_local_to_ptr(self.NewInstance(context)); +} + v8::Object* v8__Object__New(v8::Isolate* isolate) { return local_to_ptr(v8::Object::New(isolate)); } diff --git a/src/function.rs b/src/function.rs index a7c41184..b26fa9d3 100644 --- a/src/function.rs +++ b/src/function.rs @@ -10,8 +10,6 @@ use crate::support::{int, Opaque}; use crate::Context; use crate::Function; use crate::FunctionCallbackScope; -use crate::FunctionTemplate; -use crate::Isolate; use crate::Local; use crate::Name; use crate::Object; @@ -32,15 +30,6 @@ extern "C" { argv: *const Local, ) -> *mut Value; - fn v8__FunctionTemplate__New( - isolate: &Isolate, - callback: FunctionCallback, - ) -> *mut FunctionTemplate; - fn v8__FunctionTemplate__GetFunction( - fn_template: *mut FunctionTemplate, - context: *mut Context, - ) -> *mut Function; - fn v8__FunctionCallbackInfo__GetReturnValue( info: *const FunctionCallbackInfo, ) -> *mut Value; @@ -282,31 +271,6 @@ where } } -impl FunctionTemplate { - /// Creates a function template. - pub fn new<'sc>( - scope: &mut impl ToLocal<'sc>, - callback: impl MapFnTo, - ) -> Local<'sc, FunctionTemplate> { - let ptr = unsafe { - v8__FunctionTemplate__New(scope.isolate(), callback.map_fn_to()) - }; - unsafe { scope.to_local(ptr) }.unwrap() - } - - /// Returns the unique function instance in the current execution context. - pub fn get_function<'sc>( - &mut self, - scope: &mut impl ToLocal<'sc>, - mut context: Local, - ) -> Option> { - unsafe { - scope - .to_local(v8__FunctionTemplate__GetFunction(&mut *self, &mut *context)) - } - } -} - impl Function { // TODO: add remaining arguments from C++ /// Create a function in the current execution context diff --git a/src/lib.rs b/src/lib.rs index b6d5f151..1b0d8297 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,6 +66,7 @@ mod shared_array_buffer; mod snapshot; mod string; mod support; +mod template; mod try_catch; mod uint8_array; mod value; @@ -124,6 +125,7 @@ pub use support::MaybeBool; pub use support::SharedRef; pub use support::UniquePtr; pub use support::UniqueRef; +pub use template::*; pub use try_catch::{TryCatch, TryCatchScope}; // TODO(piscisaureus): Ideally this trait would not be exported. diff --git a/src/template.rs b/src/template.rs new file mode 100644 index 00000000..7b30696e --- /dev/null +++ b/src/template.rs @@ -0,0 +1,96 @@ +use crate::data::Data; +use crate::data::FunctionTemplate; +use crate::data::Name; +use crate::data::ObjectTemplate; +use crate::data::Template; +use crate::isolate::Isolate; +use crate::support::MapFnTo; +use crate::Context; +use crate::Function; +use crate::FunctionCallback; +use crate::Local; +use crate::Object; +use crate::ToLocal; + +extern "C" { + fn v8__Template__Set(self_: &Template, key: *const Name, value: *const Data); + + fn v8__FunctionTemplate__New( + isolate: &Isolate, + callback: FunctionCallback, + ) -> *mut FunctionTemplate; + fn v8__FunctionTemplate__GetFunction( + fn_template: *mut FunctionTemplate, + context: *mut Context, + ) -> *mut Function; + + fn v8__ObjectTemplate__New( + isolate: *mut Isolate, + templ: *const FunctionTemplate, + ) -> *mut ObjectTemplate; + fn v8__ObjectTemplate__NewInstance( + self_: &ObjectTemplate, + context: *mut Context, + ) -> *mut Object; +} + +impl Template { + /// Adds a property to each instance created by this template. + pub fn set(&self, key: Local, value: Local) { + // TODO(bnoordhuis) Allow setting PropertyAttributes. + unsafe { v8__Template__Set(self, &*key, &*value) } + } +} + +impl FunctionTemplate { + /// Creates a function template. + pub fn new<'sc>( + scope: &mut impl ToLocal<'sc>, + callback: impl MapFnTo, + ) -> Local<'sc, FunctionTemplate> { + let ptr = unsafe { + v8__FunctionTemplate__New(scope.isolate(), callback.map_fn_to()) + }; + unsafe { scope.to_local(ptr) }.unwrap() + } + + /// Returns the unique function instance in the current execution context. + pub fn get_function<'sc>( + &mut self, + scope: &mut impl ToLocal<'sc>, + mut context: Local, + ) -> Option> { + unsafe { + scope + .to_local(v8__FunctionTemplate__GetFunction(&mut *self, &mut *context)) + } + } +} + +impl ObjectTemplate { + /// Creates an object template. + pub fn new<'sc>(scope: &mut impl ToLocal<'sc>) -> Local<'sc, ObjectTemplate> { + let ptr = + unsafe { v8__ObjectTemplate__New(scope.isolate(), std::ptr::null()) }; + unsafe { scope.to_local(ptr) }.unwrap() + } + + /// Creates an object template from a function template. + pub fn new_from_template<'sc>( + scope: &mut impl ToLocal<'sc>, + templ: Local, + ) -> Local<'sc, ObjectTemplate> { + let ptr = unsafe { v8__ObjectTemplate__New(scope.isolate(), &*templ) }; + unsafe { scope.to_local(ptr) }.unwrap() + } + + /// Creates a new instance of this object template. + pub fn new_instance<'a>( + &self, + scope: &mut impl ToLocal<'a>, + mut context: Local, + ) -> Option> { + let ptr = unsafe { v8__ObjectTemplate__NewInstance(self, &mut *context) }; + unsafe { scope.to_local(ptr) } + } +} diff --git a/tests/test_api.rs b/tests/test_api.rs index 0ad4b0d5..255cada2 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -856,6 +856,63 @@ fn json() { } } +#[test] +fn object_template() { + let _setup_guard = 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 object_templ = v8::ObjectTemplate::new(scope); + let function_templ = v8::FunctionTemplate::new(scope, fortytwo_callback); + let name = v8_str(scope, "f"); + object_templ.set(name.into(), function_templ.into()); + let context = v8::Context::new(scope); + let mut cs = v8::ContextScope::new(scope, context); + let scope = cs.enter(); + let object = object_templ.new_instance(scope, context).unwrap(); + assert!(!object.is_null_or_undefined()); + let name = v8_str(scope, "g"); + context + .global(scope) + .set(context, name.into(), object.into()); + let actual = eval(scope, context, "g.f()").unwrap(); + let expected = v8::Integer::new(scope, 42); + assert!(expected.strict_equals(actual)); + } +} + +#[test] +fn object_template_from_function_template() { + let _setup_guard = 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 function_templ = v8::FunctionTemplate::new(scope, fortytwo_callback); + let object_templ = + v8::ObjectTemplate::new_from_template(scope, function_templ); + let context = v8::Context::new(scope); + let mut cs = v8::ContextScope::new(scope, context); + let scope = cs.enter(); + let object = object_templ.new_instance(scope, context).unwrap(); + assert!(!object.is_null_or_undefined()); + let name = v8_str(scope, "g"); + context + .global(scope) + .set(context, name.into(), object.into()); + let actual = eval(scope, context, "g.constructor.name").unwrap(); + let expected = v8::String::new(scope, "").unwrap(); + assert!(expected.strict_equals(actual)); + } +} + #[test] fn object() { let _setup_guard = setup(); @@ -1122,6 +1179,14 @@ fn fn_callback2( rv.set(s.into()); } +fn fortytwo_callback( + scope: v8::FunctionCallbackScope, + _: v8::FunctionCallbackArguments, + mut rv: v8::ReturnValue, +) { + rv.set(v8::Integer::new(scope, 42).into()); +} + #[test] fn function() { let _setup_guard = setup();