Commit bf3dd3cc authored by aa@chromium.org's avatar aa@chromium.org

Gin: Add support for binding JS methods to C++ instance methods.

Also:
- Added support for computed properties to ObjectTemplateBuilder
- Realized it was possible for CreateFunctionTemplate to be just one template

BUG=
R=abarth@chromium.org, jyasskin@chromium.org

Review URL: https://codereview.chromium.org/103703002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@239126 0039d316-1c4b-4281-b951-d872f2087c98
parent c80725d5
......@@ -28,6 +28,39 @@ typedef integral_constant<bool, false> false_type;
template <class T> struct is_pointer : false_type {};
template <class T> struct is_pointer<T*> : true_type {};
// Member function pointer detection up to four params. Add more as needed
// below. This is built-in to C++ 11, and we can remove this when we switch.
template<typename T>
struct is_member_function_pointer : false_type {};
template <typename R, typename Z>
struct is_member_function_pointer<R(Z::*)()> : true_type {};
template <typename R, typename Z>
struct is_member_function_pointer<R(Z::*)() const> : true_type {};
template <typename R, typename Z, typename A>
struct is_member_function_pointer<R(Z::*)(A)> : true_type {};
template <typename R, typename Z, typename A>
struct is_member_function_pointer<R(Z::*)(A) const> : true_type {};
template <typename R, typename Z, typename A, typename B>
struct is_member_function_pointer<R(Z::*)(A, B)> : true_type {};
template <typename R, typename Z, typename A, typename B>
struct is_member_function_pointer<R(Z::*)(A, B) const> : true_type {};
template <typename R, typename Z, typename A, typename B, typename C>
struct is_member_function_pointer<R(Z::*)(A, B, C)> : true_type {};
template <typename R, typename Z, typename A, typename B, typename C>
struct is_member_function_pointer<R(Z::*)(A, B, C) const> : true_type {};
template <typename R, typename Z, typename A, typename B, typename C,
typename D>
struct is_member_function_pointer<R(Z::*)(A, B, C, D)> : true_type {};
template <typename R, typename Z, typename A, typename B, typename C,
typename D>
struct is_member_function_pointer<R(Z::*)(A, B, C, D) const> : true_type {};
template <class T, class U> struct is_same : public false_type {};
template <class T> struct is_same<T,T> : true_type {};
......
......@@ -76,5 +76,55 @@ COMPILE_ASSERT(!is_class<char*>::value, IsClass);
COMPILE_ASSERT(!is_class<int&>::value, IsClass);
COMPILE_ASSERT(!is_class<char[3]>::value, IsClass);
COMPILE_ASSERT(!is_member_function_pointer<int>::value,
IsMemberFunctionPointer);
COMPILE_ASSERT(!is_member_function_pointer<int*>::value,
IsMemberFunctionPointer);
COMPILE_ASSERT(!is_member_function_pointer<void*>::value,
IsMemberFunctionPointer);
COMPILE_ASSERT(!is_member_function_pointer<AStruct>::value,
IsMemberFunctionPointer);
COMPILE_ASSERT(!is_member_function_pointer<AStruct*>::value,
IsMemberFunctionPointer);
COMPILE_ASSERT(!is_member_function_pointer<int(*)(int)>::value,
IsMemberFunctionPointer);
COMPILE_ASSERT(!is_member_function_pointer<int(*)(int, int)>::value,
IsMemberFunctionPointer);
COMPILE_ASSERT(is_member_function_pointer<void (AStruct::*)()>::value,
IsMemberFunctionPointer);
COMPILE_ASSERT(is_member_function_pointer<void (AStruct::*)(int)>::value,
IsMemberFunctionPointer);
COMPILE_ASSERT(is_member_function_pointer<int (AStruct::*)(int)>::value,
IsMemberFunctionPointer);
COMPILE_ASSERT(is_member_function_pointer<int (AStruct::*)(int) const>::value,
IsMemberFunctionPointer);
COMPILE_ASSERT(is_member_function_pointer<int (AStruct::*)(int, int)>::value,
IsMemberFunctionPointer);
COMPILE_ASSERT(is_member_function_pointer<
int (AStruct::*)(int, int) const>::value,
IsMemberFunctionPointer);
COMPILE_ASSERT(is_member_function_pointer<
int (AStruct::*)(int, int, int)>::value,
IsMemberFunctionPointer);
COMPILE_ASSERT(is_member_function_pointer<
int (AStruct::*)(int, int, int) const>::value,
IsMemberFunctionPointer);
COMPILE_ASSERT(is_member_function_pointer<
int (AStruct::*)(int, int, int, int)>::value,
IsMemberFunctionPointer);
COMPILE_ASSERT(is_member_function_pointer<
int (AStruct::*)(int, int, int, int) const>::value,
IsMemberFunctionPointer);
// False because we don't have a specialization for 5 params yet.
COMPILE_ASSERT(!is_member_function_pointer<
int (AStruct::*)(int, int, int, int, int)>::value,
IsMemberFunctionPointer);
COMPILE_ASSERT(!is_member_function_pointer<
int (AStruct::*)(int, int, int, int, int) const>::value,
IsMemberFunctionPointer);
} // namespace
} // namespace base
......@@ -45,10 +45,4 @@ void Arguments::ThrowTypeError(const std::string& message) {
StringToV8(isolate_, message)));
}
template<>
bool Arguments::GetNext<Arguments>(Arguments* out) {
*out = *this;
return true;
}
} // namespace gin
......@@ -20,8 +20,7 @@ class Arguments {
~Arguments();
template<typename T>
// TODO(aa): Rename GetHolder().
bool Holder(T* out) {
bool GetHolder(T* out) {
return ConvertFromV8(isolate_, info_->Holder(), out);
}
......@@ -75,9 +74,6 @@ class Arguments {
bool insufficient_arguments_;
};
template<>
bool Arguments::GetNext<Arguments>(Arguments* out);
} // namespace gin
#endif // GIN_ARGUMENTS_H_
......@@ -26,15 +26,23 @@ namespace gin {
class PerIsolateData;
enum CreateFunctionTemplateFlags {
HolderIsFirstArgument = 1 << 0,
};
namespace internal {
template<typename T>
struct RemoveConstRef {
typedef T Type;
struct CallbackParamTraits {
typedef T LocalType;
};
template<typename T>
struct CallbackParamTraits<const T&> {
typedef T LocalType;
};
template<typename T>
struct RemoveConstRef<const T&> {
typedef T Type;
struct CallbackParamTraits<const T*> {
typedef T* LocalType;
};
......@@ -58,9 +66,10 @@ class CallbackHolderBase : public Wrappable {
template<typename Sig>
class CallbackHolder : public CallbackHolderBase {
public:
CallbackHolder(const base::Callback<Sig>& callback)
: callback(callback) {}
CallbackHolder(const base::Callback<Sig>& callback, int flags)
: callback(callback), flags(flags) {}
base::Callback<Sig> callback;
int flags;
private:
virtual ~CallbackHolder() {}
};
......@@ -180,108 +189,141 @@ struct Invoker<void, void, void, void, void> {
};
template<typename T>
bool GetNextArgument(Arguments* args, int create_flags, bool is_first,
T* result) {
if (is_first && (create_flags & HolderIsFirstArgument) != 0) {
return args->GetHolder(result);
} else {
return args->GetNext(result);
}
}
// For advanced use cases, we allow callers to request the unparsed Arguments
// object and poke around in it directly.
inline bool GetNextArgument(Arguments* args, int create_flags, bool is_first,
Arguments* result) {
*result = *args;
return true;
}
// DispatchToCallback converts all the JavaScript arguments to C++ types and
// invokes the base::Callback.
template<typename Sig>
struct Dispatcher {
};
template<typename R>
static void DispatchToCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
Arguments args(info);
CallbackHolderBase* holder_base = NULL;
CHECK(args.GetData(&holder_base));
struct Dispatcher<R()> {
static void DispatchToCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
Arguments args(info);
CallbackHolderBase* holder_base = NULL;
CHECK(args.GetData(&holder_base));
typedef CallbackHolder<R()> HolderT;
HolderT* holder = static_cast<HolderT*>(holder_base);
typedef CallbackHolder<R()> HolderT;
HolderT* holder = static_cast<HolderT*>(holder_base);
Invoker<R>::Go(&args, holder->callback);
}
Invoker<R>::Go(&args, holder->callback);
}
};
template<typename R, typename P1>
static void DispatchToCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
Arguments args(info);
CallbackHolderBase* holder_base = NULL;
CHECK(args.GetData(&holder_base));
typedef CallbackHolder<R(P1)> HolderT;
HolderT* holder = static_cast<HolderT*>(holder_base);
typename RemoveConstRef<P1>::Type a1;
if (!args.GetNext(&a1)) {
args.ThrowError();
return;
struct Dispatcher<R(P1)> {
static void DispatchToCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
Arguments args(info);
CallbackHolderBase* holder_base = NULL;
CHECK(args.GetData(&holder_base));
typedef CallbackHolder<R(P1)> HolderT;
HolderT* holder = static_cast<HolderT*>(holder_base);
typename CallbackParamTraits<P1>::LocalType a1;
if (!GetNextArgument(&args, holder->flags, true, &a1)) {
args.ThrowError();
return;
}
Invoker<R, P1>::Go(&args, holder->callback, a1);
}
Invoker<R, P1>::Go(&args, holder->callback, a1);
}
};
template<typename R, typename P1, typename P2>
static void DispatchToCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
Arguments args(info);
CallbackHolderBase* holder_base = NULL;
CHECK(args.GetData(&holder_base));
typedef CallbackHolder<R(P1, P2)> HolderT;
HolderT* holder = static_cast<HolderT*>(holder_base);
typename RemoveConstRef<P1>::Type a1;
typename RemoveConstRef<P2>::Type a2;
if (!args.GetNext(&a1) ||
!args.GetNext(&a2)) {
args.ThrowError();
return;
struct Dispatcher<R(P1, P2)> {
static void DispatchToCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
Arguments args(info);
CallbackHolderBase* holder_base = NULL;
CHECK(args.GetData(&holder_base));
typedef CallbackHolder<R(P1, P2)> HolderT;
HolderT* holder = static_cast<HolderT*>(holder_base);
typename CallbackParamTraits<P1>::LocalType a1;
typename CallbackParamTraits<P2>::LocalType a2;
if (!GetNextArgument(&args, holder->flags, true, &a1) ||
!GetNextArgument(&args, holder->flags, false, &a2)) {
args.ThrowError();
return;
}
Invoker<R, P1, P2>::Go(&args, holder->callback, a1, a2);
}
Invoker<R, P1, P2>::Go(&args, holder->callback, a1, a2);
}
};
template<typename R, typename P1, typename P2, typename P3>
static void DispatchToCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
Arguments args(info);
CallbackHolderBase* holder_base = NULL;
CHECK(args.GetData(&holder_base));
typedef CallbackHolder<R(P1, P2, P3)> HolderT;
HolderT* holder = static_cast<HolderT*>(holder_base);
typename RemoveConstRef<P1>::Type a1;
typename RemoveConstRef<P2>::Type a2;
typename RemoveConstRef<P3>::Type a3;
if (!args.GetNext(&a1) ||
!args.GetNext(&a2) ||
!args.GetNext(&a3)) {
args.ThrowError();
return;
struct Dispatcher<R(P1, P2, P3)> {
static void DispatchToCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
Arguments args(info);
CallbackHolderBase* holder_base = NULL;
CHECK(args.GetData(&holder_base));
typedef CallbackHolder<R(P1, P2, P3)> HolderT;
HolderT* holder = static_cast<HolderT*>(holder_base);
typename CallbackParamTraits<P1>::LocalType a1;
typename CallbackParamTraits<P2>::LocalType a2;
typename CallbackParamTraits<P3>::LocalType a3;
if (!GetNextArgument(&args, holder->flags, true, &a1) ||
!GetNextArgument(&args, holder->flags, false, &a2) ||
!GetNextArgument(&args, holder->flags, false, &a3)) {
args.ThrowError();
return;
}
Invoker<R, P1, P2, P3>::Go(&args, holder->callback, a1, a2, a3);
}
Invoker<R, P1, P2, P3>::Go(&args, holder->callback, a1, a2, a3);
}
};
template<typename R, typename P1, typename P2, typename P3, typename P4>
static void DispatchToCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
Arguments args(info);
CallbackHolderBase* holder_base = NULL;
CHECK(args.GetData(&holder_base));
typedef CallbackHolder<R(P1, P2, P3, P4)> HolderT;
HolderT* holder = static_cast<HolderT*>(holder_base);
typename RemoveConstRef<P1>::Type a1;
typename RemoveConstRef<P2>::Type a2;
typename RemoveConstRef<P3>::Type a3;
typename RemoveConstRef<P4>::Type a4;
if (!args.GetNext(&a1) ||
!args.GetNext(&a2) ||
!args.GetNext(&a3) ||
!args.GetNext(&a4)) {
args.ThrowError();
return;
struct Dispatcher<R(P1, P2, P3, P4)> {
static void DispatchToCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
Arguments args(info);
CallbackHolderBase* holder_base = NULL;
CHECK(args.GetData(&holder_base));
typedef CallbackHolder<R(P1, P2, P3, P4)> HolderT;
HolderT* holder = static_cast<HolderT*>(holder_base);
typename CallbackParamTraits<P1>::LocalType a1;
typename CallbackParamTraits<P2>::LocalType a2;
typename CallbackParamTraits<P3>::LocalType a3;
typename CallbackParamTraits<P4>::LocalType a4;
if (!GetNextArgument(&args, holder->flags, true, &a1) ||
!GetNextArgument(&args, holder->flags, false, &a2) ||
!GetNextArgument(&args, holder->flags, false, &a3) ||
!GetNextArgument(&args, holder->flags, false, &a4)) {
args.ThrowError();
return;
}
Invoker<R, P1, P2, P3, P4>::Go(&args, holder->callback, a1, a2, a3, a4);
}
Invoker<R, P1, P2, P3, P4>::Go(&args, holder->callback, a1, a2, a3, a4);
}
};
} // namespace internal
......@@ -299,66 +341,19 @@ struct Converter<internal::CallbackHolderBase*>
: public WrappableConverter<internal::CallbackHolderBase> {};
// Creates a v8::FunctionTemplate that will run the provided base::Callback each
// time it is called. JavaScript arguments and return values are converted via
// gin::Converter.
template<typename R>
v8::Local<v8::FunctionTemplate> CreateFunctionTemplate(
v8::Isolate* isolate,
const base::Callback<R()> callback) {
typedef internal::CallbackHolder<R()> HolderT;
gin::Handle<HolderT> holder = CreateHandle(isolate, new HolderT(callback));
return v8::FunctionTemplate::New(
isolate,
&internal::DispatchToCallback<R>,
ConvertToV8<internal::CallbackHolderBase*>(isolate, holder.get()));
}
template<typename R, typename P1>
v8::Local<v8::FunctionTemplate> CreateFunctionTemplate(
v8::Isolate* isolate,
const base::Callback<R(P1)> callback) {
typedef internal::CallbackHolder<R(P1)> HolderT;
gin::Handle<HolderT> holder = CreateHandle(isolate, new HolderT(callback));
return v8::FunctionTemplate::New(
isolate,
&internal::DispatchToCallback<R, P1>,
ConvertToV8<internal::CallbackHolderBase*>(isolate, holder.get()));
}
template<typename R, typename P1, typename P2>
v8::Local<v8::FunctionTemplate> CreateFunctionTemplate(
v8::Isolate* isolate,
const base::Callback<R(P1, P2)> callback) {
typedef internal::CallbackHolder<R(P1, P2)> HolderT;
gin::Handle<HolderT> holder = CreateHandle(isolate, new HolderT(callback));
return v8::FunctionTemplate::New(
isolate,
&internal::DispatchToCallback<R, P1, P2>,
ConvertToV8<internal::CallbackHolderBase*>(isolate, holder.get()));
}
template<typename R, typename P1, typename P2, typename P3>
v8::Local<v8::FunctionTemplate> CreateFunctionTemplate(
v8::Isolate* isolate,
const base::Callback<R(P1, P2, P3)> callback) {
typedef internal::CallbackHolder<R(P1, P2, P3)> HolderT;
gin::Handle<HolderT> holder = CreateHandle(isolate, new HolderT(callback));
return v8::FunctionTemplate::New(
isolate,
&internal::DispatchToCallback<R, P1, P2, P3>,
ConvertToV8<internal::CallbackHolderBase*>(isolate, holder.get()));
}
template<typename R, typename P1, typename P2, typename P3, typename P4>
// CreateFunctionTemplate creates a v8::FunctionTemplate that will create
// JavaScript functions that execute a provided C++ function or base::Callback.
// JavaScript arguments are automatically converted via gin::Converter, as is
// the return value of the C++ function, if any.
template<typename Sig>
v8::Local<v8::FunctionTemplate> CreateFunctionTemplate(
v8::Isolate* isolate,
const base::Callback<R(P1, P2, P3, P4)> callback) {
typedef internal::CallbackHolder<R(P1, P2, P3, P4)> HolderT;
gin::Handle<HolderT> holder = CreateHandle(isolate, new HolderT(callback));
v8::Isolate* isolate, const base::Callback<Sig> callback,
int callback_flags = 0) {
typedef internal::CallbackHolder<Sig> HolderT;
gin::Handle<HolderT> holder = CreateHandle(
isolate, new HolderT(callback, callback_flags));
return v8::FunctionTemplate::New(
isolate,
&internal::DispatchToCallback<R, P1, P2, P3, P4>,
&internal::Dispatcher<Sig>::DispatchToCallback,
ConvertToV8<internal::CallbackHolderBase*>(isolate, holder.get()));
}
......
......@@ -29,15 +29,23 @@ namespace gin {
class PerIsolateData;
enum CreateFunctionTemplateFlags {
HolderIsFirstArgument = 1 << 0,
};
namespace internal {
template<typename T>
struct RemoveConstRef {
typedef T Type;
struct CallbackParamTraits {
typedef T LocalType;
};
template<typename T>
struct CallbackParamTraits<const T&> {
typedef T LocalType;
};
template<typename T>
struct RemoveConstRef<const T&> {
typedef T Type;
struct CallbackParamTraits<const T*> {
typedef T* LocalType;
};
......@@ -61,9 +69,10 @@ class CallbackHolderBase : public Wrappable {
template<typename Sig>
class CallbackHolder : public CallbackHolderBase {
public:
CallbackHolder(const base::Callback<Sig>& callback)
: callback(callback) {}
CallbackHolder(const base::Callback<Sig>& callback, int flags)
: callback(callback), flags(flags) {}
base::Callback<Sig> callback;
int flags;
private:
virtual ~CallbackHolder() {}
};
......@@ -111,39 +120,63 @@ struct Invoker<void$for ARG [[, P$(ARG)]]$for VOID [[, void]]> {
]]
template<typename T>
bool GetNextArgument(Arguments* args, int create_flags, bool is_first,
T* result) {
if (is_first && (create_flags & HolderIsFirstArgument) != 0) {
return args->GetHolder(result);
} else {
return args->GetNext(result);
}
}
// For advanced use cases, we allow callers to request the unparsed Arguments
// object and poke around in it directly.
inline bool GetNextArgument(Arguments* args, int create_flags, bool is_first,
Arguments* result) {
*result = *args;
return true;
}
// DispatchToCallback converts all the JavaScript arguments to C++ types and
// invokes the base::Callback.
template<typename Sig>
struct Dispatcher {
};
$range ARITY 0..MAX_ARITY
$for ARITY [[
$range ARG 1..ARITY
template<typename R$for ARG [[, typename P$(ARG)]]>
static void DispatchToCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
Arguments args(info);
CallbackHolderBase* holder_base = NULL;
CHECK(args.GetData(&holder_base));
struct Dispatcher<R($for ARG , [[P$(ARG)]])> {
static void DispatchToCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
Arguments args(info);
CallbackHolderBase* holder_base = NULL;
CHECK(args.GetData(&holder_base));
typedef CallbackHolder<R($for ARG , [[P$(ARG)]])> HolderT;
HolderT* holder = static_cast<HolderT*>(holder_base);
typedef CallbackHolder<R($for ARG , [[P$(ARG)]])> HolderT;
HolderT* holder = static_cast<HolderT*>(holder_base);
$if ARITY != 0 [[
$for ARG [[ typename RemoveConstRef<P$(ARG)>::Type a$(ARG);
$for ARG [[ typename CallbackParamTraits<P$(ARG)>::LocalType a$(ARG);
]]
if (
$for ARG ||
[[!args.GetNext(&a$(ARG))]]) {
args.ThrowError();
return;
}
if ($for ARG ||
[[!GetNextArgument(&args, holder->flags, $if ARG == 1 [[true]] $else [[false]], &a$(ARG))]]) {
args.ThrowError();
return;
}
]]
Invoker<R$for ARG [[, P$(ARG)]]>::Go(&args, holder->callback$for ARG [[, a$(ARG)]]);
}
Invoker<R$for ARG [[, P$(ARG)]]>::Go(&args, holder->callback$for ARG [[, a$(ARG)]]);
}
};
]]
......@@ -163,27 +196,22 @@ struct Converter<internal::CallbackHolderBase*>
: public WrappableConverter<internal::CallbackHolderBase> {};
// Creates a v8::FunctionTemplate that will run the provided base::Callback each
// time it is called. JavaScript arguments and return values are converted via
// gin::Converter.
$range ARITY 0..MAX_ARITY
$for ARITY [[
$range ARG 1..ARITY
template<typename R$for ARG [[, typename P$(ARG)]]>
// CreateFunctionTemplate creates a v8::FunctionTemplate that will create
// JavaScript functions that execute a provided C++ function or base::Callback.
// JavaScript arguments are automatically converted via gin::Converter, as is