diff --git a/src/binding.cc b/src/binding.cc index 176c95a9..d250a7e1 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -1582,6 +1582,26 @@ void v8__FunctionTemplate__SetClassName(const v8::FunctionTemplate& self, ptr_to_local(&self)->SetClassName(ptr_to_local(&name)); } +void v8__FunctionTemplate__Inherit(const v8::FunctionTemplate& self, + const v8::FunctionTemplate& parent) { + ptr_to_local(&self)->Inherit(ptr_to_local(&parent)); +} + +void v8__FunctionTemplate__ReadOnlyPrototype(const v8::FunctionTemplate& self) { + ptr_to_local(&self)->ReadOnlyPrototype(); +} + +void v8__FunctionTemplate__RemovePrototype(const v8::FunctionTemplate& self) { + ptr_to_local(&self)->RemovePrototype(); +} + +const v8::ObjectTemplate* v8__FunctionTemplate__PrototypeTemplate( + const v8::FunctionTemplate& self) { + + return local_to_ptr( + ptr_to_local(&self)->PrototypeTemplate()); +} + v8::Isolate* v8__FunctionCallbackInfo__GetIsolate( const v8::FunctionCallbackInfo& self) { return self.GetIsolate(); diff --git a/src/template.rs b/src/template.rs index 685439a9..86da73d8 100644 --- a/src/template.rs +++ b/src/template.rs @@ -51,10 +51,19 @@ extern "C" { this: *const FunctionTemplate, context: *const Context, ) -> *const Function; + fn v8__FunctionTemplate__PrototypeTemplate( + this: *const FunctionTemplate, + ) -> *const ObjectTemplate; fn v8__FunctionTemplate__SetClassName( this: *const FunctionTemplate, name: *const String, ); + fn v8__FunctionTemplate__Inherit( + this: *const FunctionTemplate, + parent: *const FunctionTemplate, + ); + fn v8__FunctionTemplate__ReadOnlyPrototype(this: *const FunctionTemplate); + fn v8__FunctionTemplate__RemovePrototype(this: *const FunctionTemplate); fn v8__ObjectTemplate__New( isolate: *mut Isolate, @@ -184,6 +193,35 @@ impl FunctionTemplate { pub fn set_class_name(&self, name: Local) { unsafe { v8__FunctionTemplate__SetClassName(self, &*name) }; } + + /// Returns the ObjectTemplate that is used by this + /// FunctionTemplate as a PrototypeTemplate + pub fn prototype_template<'s>( + &self, + scope: &mut HandleScope<'s, ()>, + ) -> Local<'s, ObjectTemplate> { + unsafe { + scope.cast_local(|_sd| v8__FunctionTemplate__PrototypeTemplate(self)) + } + .unwrap() + } + + /// Causes the function template to inherit from a parent function template. + /// This means the function's prototype.__proto__ is set to the parent function's prototype. + pub fn inherit(&self, parent: Local) { + unsafe { v8__FunctionTemplate__Inherit(self, &*parent) }; + } + + /// Sets the ReadOnly flag in the attributes of the 'prototype' property + /// of functions created from this FunctionTemplate to true. + pub fn read_only_prototype(&self) { + unsafe { v8__FunctionTemplate__ReadOnlyPrototype(self) }; + } + + /// Removes the prototype property from functions created from this FunctionTemplate. + pub fn remove_prototype(&self) { + unsafe { v8__FunctionTemplate__RemovePrototype(self) }; + } } impl ObjectTemplate { diff --git a/tests/test_api.rs b/tests/test_api.rs index eba9f94e..29ee1147 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -1330,6 +1330,79 @@ fn function_template_signature() { } } +#[test] +fn function_template_prototype() { + 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 scope = &mut v8::TryCatch::new(scope); + let function_templ = v8::FunctionTemplate::new(scope, fortytwo_callback); + let prototype_templ = function_templ.prototype_template(scope); + + let amount_name = v8::String::new(scope, "amount").unwrap(); + let value = v8::Number::new(scope, 1.0); + let second_value = v8::Number::new(scope, 2.0); + let third_value = v8::Number::new(scope, 3.0); + prototype_templ.set(amount_name.into(), value.into()); + + let function = function_templ.get_function(scope).unwrap(); + function.new_instance(scope, &[]); + + let object1 = function.new_instance(scope, &[]).unwrap(); + assert!(!object1.is_null_or_undefined()); + let name = v8::String::new(scope, "ob1").unwrap(); + context + .global(scope) + .set(scope, name.into(), object1.into()); + + let actual_amount = + eval(scope, "ob1.amount").unwrap().to_number(scope).unwrap(); + dbg!("{}", actual_amount.number_value(scope).unwrap()); + assert!(value.eq(&actual_amount)); + + let object2 = function.new_instance(scope, &[]).unwrap(); + assert!(!object2.is_null_or_undefined()); + let name = v8::String::new(scope, "ob2").unwrap(); + context + .global(scope) + .set(scope, name.into(), object2.into()); + + let actual_amount = + eval(scope, "ob2.amount").unwrap().to_number(scope).unwrap(); + dbg!("{}", actual_amount.number_value(scope).unwrap()); + assert!(value.eq(&actual_amount)); + + eval(scope, "ob1.amount = 2").unwrap(); + + let actual_amount = + eval(scope, "ob1.amount").unwrap().to_number(scope).unwrap(); + dbg!("{}", actual_amount.number_value(scope).unwrap()); + assert!(second_value.eq(&actual_amount)); + + // We need to get the prototype of the object to change it, it is not the same object as the prototype template! + object2 + .get_prototype(scope) + .unwrap() + .to_object(scope) + .unwrap() + .set(scope, amount_name.into(), third_value.into()); + + let actual_amount = + eval(scope, "ob1.amount").unwrap().to_number(scope).unwrap(); + dbg!("{}", actual_amount.number_value(scope).unwrap()); + assert!(second_value.eq(&actual_amount)); + + let actual_amount = + eval(scope, "ob2.amount").unwrap().to_number(scope).unwrap(); + dbg!("{}", actual_amount.number_value(scope).unwrap()); + assert!(third_value.eq(&actual_amount)); + } +} + #[test] fn object_template_set_accessor() { let _setup_guard = setup();