| // Copyright 2013 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef GIN_OBJECT_TEMPLATE_BUILDER_H_ |
| #define GIN_OBJECT_TEMPLATE_BUILDER_H_ |
| |
| #include <string_view> |
| #include <type_traits> |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/memory/raw_ptr.h" |
| #include "gin/converter.h" |
| #include "gin/function_template.h" |
| #include "gin/gin_export.h" |
| #include "gin/public/wrappable_pointer_tags.h" |
| #include "v8/include/v8-forward.h" |
| #include "v8/include/v8-template.h" |
| |
| namespace gin { |
| |
| namespace internal { |
| |
| template <typename T> |
| v8::Local<v8::FunctionTemplate> CreateFunctionTemplate(v8::Isolate* isolate, |
| T callback, |
| const char* type_name) { |
| // We need to handle member function pointers case specially because the first |
| // parameter for callbacks to MFP should typically come from the the |
| // JavaScript "this" object the function was called on, not from the first |
| // normal parameter. |
| InvokerOptions options; |
| if (std::is_member_function_pointer<T>::value) { |
| options.holder_is_first_argument = true; |
| options.holder_type = type_name; |
| } |
| return ::gin::CreateFunctionTemplate( |
| isolate, base::BindRepeating(std::move(callback)), std::move(options)); |
| } |
| |
| } // namespace internal |
| |
| // ObjectTemplateBuilder provides a handy interface to creating |
| // v8::ObjectTemplate instances with various sorts of properties. |
| class GIN_EXPORT ObjectTemplateBuilder { |
| public: |
| explicit ObjectTemplateBuilder(v8::Isolate* isolate); |
| ObjectTemplateBuilder(v8::Isolate* isolate, const char* type_name); |
| ObjectTemplateBuilder(const ObjectTemplateBuilder& other); |
| ~ObjectTemplateBuilder(); |
| |
| // It's against Google C++ style to return a non-const ref, but we take some |
| // poetic license here in order that all calls to Set() can be via the '.' |
| // operator and line up nicely. |
| template <typename T> |
| ObjectTemplateBuilder& SetValue(const std::string_view& name, T val) { |
| return SetImpl(name, ConvertToV8(isolate_, val)); |
| } |
| |
| // In the following methods, T and U can be function pointer, member function |
| // pointer, base::RepeatingCallback, or v8::FunctionTemplate. Most clients |
| // will want to use one of the first two options. Also see |
| // gin::CreateFunctionTemplate() for creating raw function templates. |
| template <typename T> |
| ObjectTemplateBuilder& SetMethod(const std::string_view& name, |
| const T& callback) { |
| return SetImpl( |
| name, internal::CreateFunctionTemplate(isolate_, callback, type_name_)); |
| } |
| |
| template <typename T> |
| ObjectTemplateBuilder& SetMethod(v8::Local<v8::Name> name, |
| const T& callback) { |
| return SetImpl( |
| name, internal::CreateFunctionTemplate(isolate_, callback, type_name_)); |
| } |
| |
| template <typename T> |
| ObjectTemplateBuilder& SetProperty(const std::string_view& name, |
| const T& getter) { |
| return SetPropertyImpl( |
| name, internal::CreateFunctionTemplate(isolate_, getter, type_name_), |
| v8::Local<v8::FunctionTemplate>()); |
| } |
| template <typename T, typename U> |
| ObjectTemplateBuilder& SetProperty(const std::string_view& name, |
| const T& getter, |
| const U& setter) { |
| return SetPropertyImpl( |
| name, internal::CreateFunctionTemplate(isolate_, getter, type_name_), |
| internal::CreateFunctionTemplate(isolate_, setter, type_name_)); |
| } |
| |
| // Whereas SetProperty creates an accessor property, this creates what appears |
| // to be a data property but whose value is lazily computed the first time the |
| // [[Get]] operation occurs. |
| template <typename T> |
| ObjectTemplateBuilder& SetLazyDataProperty(const std::string_view& name, |
| const T& getter) { |
| InvokerOptions options; |
| if (std::is_member_function_pointer<T>::value) { |
| options.holder_is_first_argument = true; |
| options.holder_type = type_name_; |
| } |
| auto [callback, data] = CreateDataPropertyCallback( |
| isolate_, base::BindRepeating(getter), std::move(options)); |
| return SetLazyDataPropertyImpl(name, callback, data); |
| } |
| |
| template <WrappablePointerTag tag> |
| ObjectTemplateBuilder& AddNamedPropertyInterceptor() { |
| template_->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| &ObjectTemplateBuilder::NamedPropertyGetter<tag>, |
| &ObjectTemplateBuilder::NamedPropertySetter<tag>, |
| &ObjectTemplateBuilder::NamedPropertyQuery<tag>, nullptr, |
| &ObjectTemplateBuilder::NamedPropertyEnumerator<tag>, |
| v8::Local<v8::Value>(), |
| v8::PropertyHandlerFlags::kOnlyInterceptStrings)); |
| return *this; |
| } |
| |
| ObjectTemplateBuilder& AddIndexedPropertyInterceptor(); |
| |
| v8::Local<v8::ObjectTemplate> Build(); |
| |
| private: |
| ObjectTemplateBuilder& SetImpl(const std::string_view& name, |
| v8::Local<v8::Data> val); |
| ObjectTemplateBuilder& SetImpl(v8::Local<v8::Name> name, |
| v8::Local<v8::Data> val); |
| ObjectTemplateBuilder& SetPropertyImpl( |
| const std::string_view& name, |
| v8::Local<v8::FunctionTemplate> getter, |
| v8::Local<v8::FunctionTemplate> setter); |
| ObjectTemplateBuilder& SetLazyDataPropertyImpl( |
| const std::string_view& name, |
| v8::AccessorNameGetterCallback callback, |
| v8::Local<v8::Value> data); |
| |
| static v8::Intercepted NamedPropertyGetterImpl( |
| WrappablePointerTag tag, |
| v8::Local<v8::Name> property, |
| const v8::PropertyCallbackInfo<v8::Value>& info); |
| static v8::Intercepted NamedPropertySetterImpl( |
| WrappablePointerTag tag, |
| v8::Local<v8::Name> property, |
| v8::Local<v8::Value> value, |
| const v8::PropertyCallbackInfo<void>& info); |
| static v8::Intercepted NamedPropertyQueryImpl( |
| WrappablePointerTag tag, |
| v8::Local<v8::Name> property, |
| const v8::PropertyCallbackInfo<v8::Integer>& info); |
| static void NamedPropertyEnumeratorImpl( |
| WrappablePointerTag tag, |
| const v8::PropertyCallbackInfo<v8::Array>& info); |
| |
| template <WrappablePointerTag tag> |
| static v8::Intercepted NamedPropertyGetter( |
| v8::Local<v8::Name> property, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| return NamedPropertyGetterImpl(tag, property, info); |
| } |
| template <WrappablePointerTag tag> |
| static v8::Intercepted NamedPropertySetter( |
| v8::Local<v8::Name> property, |
| v8::Local<v8::Value> value, |
| const v8::PropertyCallbackInfo<void>& info) { |
| return NamedPropertySetterImpl(tag, property, value, info); |
| } |
| template <WrappablePointerTag tag> |
| static v8::Intercepted NamedPropertyQuery( |
| v8::Local<v8::Name> property, |
| const v8::PropertyCallbackInfo<v8::Integer>& info) { |
| return NamedPropertyQueryImpl(tag, property, info); |
| } |
| template <WrappablePointerTag tag> |
| static void NamedPropertyEnumerator( |
| const v8::PropertyCallbackInfo<v8::Array>& info) { |
| NamedPropertyEnumeratorImpl(tag, info); |
| } |
| |
| raw_ptr<v8::Isolate> isolate_; |
| |
| // If provided, |type_name_| will be used to give a user-friendly error |
| // message if a member function is invoked on the wrong type of object. |
| const char* type_name_ = nullptr; |
| |
| // ObjectTemplateBuilder should only be used on the stack. |
| v8::Local<v8::FunctionTemplate> constructor_template_; |
| v8::Local<v8::ObjectTemplate> template_; |
| }; |
| |
| } // namespace gin |
| |
| #endif // GIN_OBJECT_TEMPLATE_BUILDER_H_ |