Skip to content

Commit

Permalink
Gin: Make it easier to implement Wrappable.
Browse files Browse the repository at this point in the history
BUG=

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@239387 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
aa@chromium.org committed Dec 8, 2013
1 parent 7e9f057 commit 6fe5610
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 151 deletions.
6 changes: 1 addition & 5 deletions gin/function_template.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@

namespace gin {

WrapperInfo internal::CallbackHolderBase::kWrapperInfo = { kEmbedderNativeGin };

WrapperInfo* internal::CallbackHolderBase::GetWrapperInfo() {
return &kWrapperInfo;
}
INIT_WRAPPABLE(internal::CallbackHolderBase);

void InitFunctionTemplates(PerIsolateData* isolate_data) {
if (!isolate_data->GetObjectTemplate(
Expand Down
13 changes: 6 additions & 7 deletions gin/function_template.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ enum CreateFunctionTemplateFlags {

namespace internal {

// TODO(aa): Move this to base/template_util.h as remove_const.
template<typename T>
struct CallbackParamTraits {
typedef T LocalType;
Expand All @@ -56,12 +55,12 @@ struct CallbackParamTraits<const T*> {
// It inherits from Wrappable, which delete itself when both (a) the refcount
// via base::RefCounted has dropped to zero, and (b) there are no more
// JavaScript references in V8.
class CallbackHolderBase : public Wrappable {
public:
virtual WrapperInfo* GetWrapperInfo() OVERRIDE;
static WrapperInfo kWrapperInfo;

// This simple base class is used so that we can share a single object template
// among every CallbackHolder instance.
class CallbackHolderBase : public Wrappable<CallbackHolderBase> {
protected:
virtual ~CallbackHolderBase() {}
~CallbackHolderBase() {}
};

template<typename Sig>
Expand All @@ -72,7 +71,7 @@ class CallbackHolder : public CallbackHolderBase {
base::Callback<Sig> callback;
int flags;
private:
virtual ~CallbackHolder() {}
~CallbackHolder() {}
};


Expand Down
12 changes: 6 additions & 6 deletions gin/function_template.h.pump
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@ struct CallbackParamTraits<const T*> {
// It inherits from Wrappable, which delete itself when both (a) the refcount
// via base::RefCounted has dropped to zero, and (b) there are no more
// JavaScript references in V8.
class CallbackHolderBase : public Wrappable {
public:
virtual WrapperInfo* GetWrapperInfo() OVERRIDE;
static WrapperInfo kWrapperInfo;

// This simple base class is used so that we can share a single object template
// among every CallbackHolder instance.
class CallbackHolderBase : public Wrappable<CallbackHolderBase> {
protected:
virtual ~CallbackHolderBase() {}
~CallbackHolderBase() {}
};

template<typename Sig>
Expand All @@ -74,7 +74,7 @@ class CallbackHolder : public CallbackHolderBase {
base::Callback<Sig> callback;
int flags;
private:
virtual ~CallbackHolder() {}
~CallbackHolder() {}
};


Expand Down
2 changes: 1 addition & 1 deletion gin/object_template_builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ struct CallbackTraits<base::Callback<T> > {
// from the first normal parameter.
template<typename T>
struct CallbackTraits<T, typename base::enable_if<
base::is_member_function_pointer<T>::value >::type> {
base::is_member_function_pointer<T>::value>::type> {
static v8::Handle<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate,
T callback) {
return CreateFunctionTemplate(isolate, base::Bind(callback),
Expand Down
48 changes: 37 additions & 11 deletions gin/wrappable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,29 @@

namespace gin {

Wrappable::Wrappable() {
WrappableBase::WrappableBase() {
}

Wrappable::~Wrappable() {
WrappableBase::~WrappableBase() {
wrapper_.Reset();
}

v8::Handle<v8::Object> Wrappable::GetWrapper(v8::Isolate* isolate) {
if (wrapper_.IsEmpty())
CreateWrapper(isolate);
return v8::Local<v8::Object>::New(isolate, wrapper_);
v8::Handle<v8::Object> WrappableBase::GetWrapperImpl(
v8::Isolate* isolate, WrapperInfo* wrapper_info) {
if (wrapper_.IsEmpty())
CreateWrapper(isolate, wrapper_info);
return v8::Local<v8::Object>::New(isolate, wrapper_);
}

void Wrappable::WeakCallback(
const v8::WeakCallbackData<v8::Object, Wrappable>& data) {
Wrappable* wrappable = data.GetParameter();
void WrappableBase::WeakCallback(
const v8::WeakCallbackData<v8::Object, WrappableBase>& data) {
WrappableBase* wrappable = data.GetParameter();
wrappable->wrapper_.Reset();
delete wrappable;
}

v8::Handle<v8::Object> Wrappable::CreateWrapper(v8::Isolate* isolate) {
WrapperInfo* info = GetWrapperInfo();
v8::Handle<v8::Object> WrappableBase::CreateWrapper(v8::Isolate* isolate,
WrapperInfo* info) {
PerIsolateData* data = PerIsolateData::From(isolate);
v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate(info);
CHECK(!templ.IsEmpty()); // Don't forget to register an object template.
Expand All @@ -43,4 +44,29 @@ v8::Handle<v8::Object> Wrappable::CreateWrapper(v8::Isolate* isolate) {
return wrapper;
}

namespace internal {

void* FromV8Impl(v8::Isolate* isolate, v8::Handle<v8::Value> val,
WrapperInfo* wrapper_info) {
if (!val->IsObject())
return NULL;
v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(val);
WrapperInfo* info = WrapperInfo::From(obj);

// If this fails, the object is not managed by Gin. It is either a normal JS
// object that's not wrapping any external C++ object, or it is wrapping some
// C++ object, but that object isn't managed by Gin (maybe Blink).
if (!info)
return NULL;

// If this fails, the object is managed by Gin, but it's not wrapping an
// instance of the C++ class associated with wrapper_info.
if (info != wrapper_info)
return NULL;

return obj->GetAlignedPointerFromInternalField(kEncodedValueIndex);
}

} // namespace internal

} // namespace gin
134 changes: 68 additions & 66 deletions gin/wrappable.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,91 +11,93 @@

namespace gin {

// Wrappable is an abstract base class for C++ objects that have cooresponding
// v8 wrapper objects. To retain a Wrappable object on the stack, use a
// gin::Handle.
class Wrappable {
public:
// Subclasses must return the WrapperInfo object associated with the
// v8::ObjectTemplate for their subclass. When creating a v8 wrapper for
// this object, we'll look up the appropriate v8::ObjectTemplate in the
// PerIsolateData using this WrapperInfo pointer.
virtual WrapperInfo* GetWrapperInfo() = 0;

// Subclasses much also contain a static member variable named |kWrapperInfo|
// of type WrapperInfo:
//
// static WrapperInfo kWrapperInfo;
//
// If |obj| is a concrete instance of the subclass, then obj->GetWrapperInfo()
// must return &kWrapperInfo.
//
// We use both the dynamic |GetWrapperInfo| function and the static
// |kWrapperInfo| member variable during wrapping and the unwrapping. During
// wrapping, we use GetWrapperInfo() to make sure we use the correct
// v8::ObjectTemplate for the object regardless of the declared C++ type.
// During unwrapping, we use the static member variable to prevent type errors
// during the downcast from Wrappable to the subclass.

// Retrieve (or create) the v8 wrapper object cooresponding to this object.
// To customize the wrapper created for a subclass, override GetWrapperInfo()
// instead of overriding this function.
v8::Handle<v8::Object> GetWrapper(v8::Isolate* isolate);
namespace internal {

void* FromV8Impl(v8::Isolate* isolate, v8::Handle<v8::Value> val,
WrapperInfo* info);

} // namespace internal


// Wrappable is a base class for C++ objects that have corresponding v8 wrapper
// objects. To retain a Wrappable object on the stack, use a gin::Handle.
//
// USAGE:
// // my_class.h
// class MyClass : Wrappable<MyClass> {
// ...
// };
//
// // my_class.cc
// INIT_WRAPABLE(MyClass);
//
// Subclasses should also typically have private constructors and expose a
// static Create function that returns a gin::Handle. Forcing creators through
// this static Create function will enforce that clients actually create a
// wrapper for the object. If clients fail to create a wrapper for a wrappable
// object, the object will leak because we use the weak callback from the
// wrapper as the signal to delete the wrapped object.
template<typename T>
class Wrappable;

// Subclasses should have private constructors and expose a static Create
// function that returns a gin::Handle. Forcing creators through this static
// Create function will enforce that clients actually create a wrapper for
// the object. If clients fail to create a wrapper for a wrappable object,
// the object will leak because we use the weak callback from the wrapper
// as the signal to delete the wrapped object.

// Non-template base class to share code between templates instances.
class WrappableBase {
protected:
Wrappable();
virtual ~Wrappable();
WrappableBase();
~WrappableBase();
v8::Handle<v8::Object> GetWrapperImpl(v8::Isolate* isolate,
WrapperInfo* wrapper_info);
v8::Handle<v8::Object> CreateWrapper(v8::Isolate* isolate,
WrapperInfo* wrapper_info);
v8::Persistent<v8::Object> wrapper_; // Weak

private:
static void WeakCallback(
const v8::WeakCallbackData<v8::Object, Wrappable>& data);
v8::Handle<v8::Object> CreateWrapper(v8::Isolate* isolate);
const v8::WeakCallbackData<v8::Object, WrappableBase>& data);

v8::Persistent<v8::Object> wrapper_; // Weak
DISALLOW_COPY_AND_ASSIGN(WrappableBase);
};


template<typename T>
class Wrappable : public WrappableBase {
public:
static WrapperInfo kWrapperInfo;

// Retrieve (or create) the v8 wrapper object cooresponding to this object.
// To customize the wrapper created for a subclass, override GetWrapperInfo()
// instead of overriding this function.
v8::Handle<v8::Object> GetWrapper(v8::Isolate* isolate) {
return GetWrapperImpl(isolate, &kWrapperInfo);
}

protected:
Wrappable() {}
~Wrappable() {}
DISALLOW_COPY_AND_ASSIGN(Wrappable);
};


// Subclasses of Wrappable must call this within a cc file to initialize their
// WrapperInfo.
#define INIT_WRAPPABLE(TYPE) \
template<> \
gin::WrapperInfo gin::Wrappable<TYPE>::kWrapperInfo = { kEmbedderNativeGin };


// This converter handles any subclass of Wrappable.
template<typename T>
struct Converter<T*, typename base::enable_if<
base::is_convertible<T*, Wrappable*>::value>::type> {
base::is_convertible<T*, Wrappable<T>*>::value>::type> {
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, T* val) {
return val->GetWrapper(isolate);
}

static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val, T** out) {
if (!val->IsObject())
return false;
v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(val);
WrapperInfo* info = WrapperInfo::From(obj);

// If this fails, the object is not managed by Gin. It is either a normal JS
// object that's not wrapping any external C++ object, or it is wrapping
// some C++ object, but that object isn't managed by Gin (maybe Blink).
if (!info)
return false;

// If this fails, the object is managed by Gin, but it's not wrapping an
// instance of T.
if (info != &T::kWrapperInfo)
return false;

void* pointer = obj->GetAlignedPointerFromInternalField(kEncodedValueIndex);
T* result = static_cast<T*>(pointer);

// If this fails, something fishy is going on. |info| should have come from
// T::GetWrapperInfo(), but somehow is now different than it. So panic.
CHECK(result->GetWrapperInfo() == info);
*out = result;
return true;
*out = static_cast<T*>(internal::FromV8Impl(isolate, val,
&T::kWrapperInfo));
return *out != NULL;
}
};

Expand Down
54 changes: 10 additions & 44 deletions gin/wrappable_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,65 +14,32 @@
#include "testing/gtest/include/gtest/gtest.h"

namespace gin {
namespace {

class MyObject : public Wrappable {
class MyObject : public Wrappable<MyObject> {
public:
static gin::Handle<MyObject> Create(v8::Isolate* isolate);
static gin::Handle<MyObject> Create(v8::Isolate* isolate) {
return CreateHandle(isolate, new MyObject());
}

int value() const { return value_; }
void set_value(int value) { value_ = value; }

static WrapperInfo kWrapperInfo;
virtual WrapperInfo* GetWrapperInfo() OVERRIDE;

private:
MyObject() : value_(0) {}
virtual ~MyObject() {}
~MyObject() {}

int value_;
};

WrapperInfo MyObject::kWrapperInfo = { kEmbedderNativeGin };

gin::Handle<MyObject> MyObject::Create(v8::Isolate* isolate) {
return CreateHandle(isolate, new MyObject());
}

WrapperInfo* MyObject::GetWrapperInfo() {
return &kWrapperInfo;
}


class MyObject2 : public Wrappable {
public:
MyObject2() {
}
static WrapperInfo kWrapperInfo;
virtual WrapperInfo* GetWrapperInfo() OVERRIDE;
class MyObject2 : public Wrappable<MyObject2> {
};

WrapperInfo MyObject2::kWrapperInfo = { kEmbedderNativeGin };

WrapperInfo* MyObject2::GetWrapperInfo() {
return &kWrapperInfo;
}


class MyObjectBlink : public Wrappable {
public:
MyObjectBlink() {
}
static WrapperInfo kWrapperInfo;
virtual WrapperInfo* GetWrapperInfo() OVERRIDE;
class MyObjectBlink : public Wrappable<MyObjectBlink> {
};

WrapperInfo MyObjectBlink::kWrapperInfo = { kEmbedderBlink };

WrapperInfo* MyObjectBlink::GetWrapperInfo() {
return &kWrapperInfo;
}

INIT_WRAPPABLE(gin::MyObject);
INIT_WRAPPABLE(gin::MyObject2);
INIT_WRAPPABLE(gin::MyObjectBlink);

void RegisterTemplates(v8::Isolate* isolate) {
PerIsolateData* data = PerIsolateData::From(isolate);
Expand Down Expand Up @@ -173,5 +140,4 @@ TEST_F(WrappableTest, GetAndSetProperty) {
EXPECT_EQ(191, obj->value());
}

} // namespace
} // namespace gin
Loading

0 comments on commit 6fe5610

Please sign in to comment.