[Gin] Allow ObjectTemplateBuilder and Wrappable to provide a typename
ObjectTemplateBuilder (and Wrappable) allow setting functions that are
exposed in JS and then forwarded to the type in C++. If these are C++
member functions, the context object is retrieved by looking at the
this-object in JavaScript, and trying to convert that to the underlying
C++ type (which will succeed if it was called on the V8 object created
by the Wrappable).
However, these member functions will fail if the JS function is called
on an improper this-object (including null and undefined). This means
that doing things like:
var obj = getObj(); // Some object from a Wrappable
var doFoo = obj.doFoo;
doFoo();
will fail because it's applied on an invalid this-object (undefined),
and the conversion will fail. This makes sense, but unfortunatley the
error gin throws in this case is unhelpful:
"Uncaught TypeError: Error processing argument at index -1, conversion
failure from undefined"
Instead, allow Wrappables and ObjectTemplateBuilders to specify a type
name, which will be surfaced in these errors to provide guidance to
developers. Update the error message to either include the type name or
to match the error message used in similar circumstances in blink
("Illegal invocation").
The new error messages will only be shown for failures in converting to
the context object, not for failure to convert subsequent arguments.
Bug: <File One>
Change-Id: I515a17e92992bfcb709b97455b9167b350e931f2
Reviewed-on: https://chromium-review.googlesource.com/987304
Commit-Queue: Devlin <[email protected]>
Reviewed-by: Jeremy Roman <[email protected]>
Cr-Commit-Position: refs/heads/master@{#549219}
diff --git a/gin/object_template_builder.h b/gin/object_template_builder.h
index 92230ea..2d67bb8 100644
--- a/gin/object_template_builder.h
+++ b/gin/object_template_builder.h
@@ -17,21 +17,27 @@
namespace gin {
+namespace internal {
+
template <typename T>
v8::Local<v8::FunctionTemplate> CreateFunctionTemplate(v8::Isolate* isolate,
- T callback) {
+ 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.
- int callback_flags = 0;
- if (std::is_member_function_pointer<T>::value)
- callback_flags = HolderIsFirstArgument;
-
- return CreateFunctionTemplate(
- isolate, base::BindRepeating(std::move(callback)), callback_flags);
+ 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
+
template <typename T>
void SetAsFunctionHandler(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> tmpl,
@@ -40,12 +46,10 @@
// 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.
- int callback_flags = 0;
- if (std::is_member_function_pointer<T>::value)
- callback_flags = HolderIsFirstArgument;
+ InvokerOptions options = {std::is_member_function_pointer<T>::value, nullptr};
CreateFunctionHandler(isolate, tmpl, base::BindRepeating(std::move(callback)),
- callback_flags);
+ std::move(options));
}
// ObjectTemplateBuilder provides a handy interface to creating
@@ -53,6 +57,7 @@
class GIN_EXPORT ObjectTemplateBuilder {
public:
explicit ObjectTemplateBuilder(v8::Isolate* isolate);
+ ObjectTemplateBuilder(v8::Isolate* isolate, const char* type_name);
ObjectTemplateBuilder(const ObjectTemplateBuilder& other);
~ObjectTemplateBuilder();
@@ -71,19 +76,22 @@
template<typename T>
ObjectTemplateBuilder& SetMethod(const base::StringPiece& name,
const T& callback) {
- return SetImpl(name, CreateFunctionTemplate(isolate_, callback));
+ return SetImpl(
+ name, internal::CreateFunctionTemplate(isolate_, callback, type_name_));
}
template<typename T>
ObjectTemplateBuilder& SetProperty(const base::StringPiece& name,
const T& getter) {
- return SetPropertyImpl(name, CreateFunctionTemplate(isolate_, getter),
- v8::Local<v8::FunctionTemplate>());
+ return SetPropertyImpl(
+ name, internal::CreateFunctionTemplate(isolate_, getter, type_name_),
+ v8::Local<v8::FunctionTemplate>());
}
template<typename T, typename U>
ObjectTemplateBuilder& SetProperty(const base::StringPiece& name,
const T& getter, const U& setter) {
- return SetPropertyImpl(name, CreateFunctionTemplate(isolate_, getter),
- CreateFunctionTemplate(isolate_, setter));
+ return SetPropertyImpl(
+ name, internal::CreateFunctionTemplate(isolate_, getter, type_name_),
+ internal::CreateFunctionTemplate(isolate_, setter, type_name_));
}
ObjectTemplateBuilder& AddNamedPropertyInterceptor();
ObjectTemplateBuilder& AddIndexedPropertyInterceptor();
@@ -99,6 +107,10 @@
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::ObjectTemplate> template_;
};