diff options
author | abarth@chromium.org <abarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-21 18:38:51 +0000 |
---|---|---|
committer | abarth@chromium.org <abarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-21 18:38:51 +0000 |
commit | 93f9f360fc88ea3bae8e74b7b22ad25d1ddaec85 (patch) | |
tree | 35d4b253f7d95749f54c3ed179bb8c99834a3f79 /gin | |
parent | 69ef3fe6c0e79036f8e6f1b740b0cd96137e461e (diff) | |
download | chromium_src-93f9f360fc88ea3bae8e74b7b22ad25d1ddaec85.zip chromium_src-93f9f360fc88ea3bae8e74b7b22ad25d1ddaec85.tar.gz chromium_src-93f9f360fc88ea3bae8e74b7b22ad25d1ddaec85.tar.bz2 |
[Gin] Add a mechanism for wrapping C++ object
This CL adds a mechanism for wrapping C++ objects to Gin. The approach in this
CL is similar to Blink's ScriptWrappable class, with a couple of differences:
1) gin::Wrappable has a vtable whereas Blink's ScriptWrappable class does not.
Having a vtable in this base class lets us simplify a large number of
concerns. We've talked about adding a vtable to ScriptWrappable but have
avoided it because Blink creates many thousands of wrapped objects. When we
refactor Blink to use Gin, we can still support the non-vtable approach, but
most clients of Gin will want the simpler approach.
2) In Gin, we've bound together the notion of being reference counted with the
notion of being wrappable from JavaScript. In Blink, those concepts are
separate because we don't want to introduce a virtual destructor for
ScriptWrappable. However, because gin::Wrappable already has a vtable,
adding a virtual destructor is relatively cheap.
Actually wrapping a C++ object still takes too much typing, but we can improve
that in future CLs.
R=jochen@chromium.org
BUG=317398
Review URL: https://codereview.chromium.org/79203004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@236555 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gin')
-rw-r--r-- | gin/array_buffer.cc | 2 | ||||
-rw-r--r-- | gin/dictionary.cc | 3 | ||||
-rw-r--r-- | gin/gin.gyp | 23 | ||||
-rw-r--r-- | gin/wrappable.cc | 63 | ||||
-rw-r--r-- | gin/wrappable.h | 60 | ||||
-rw-r--r-- | gin/wrappable_unittest.cc | 137 |
6 files changed, 276 insertions, 12 deletions
diff --git a/gin/array_buffer.cc b/gin/array_buffer.cc index 9f93bf9..8b53354 100644 --- a/gin/array_buffer.cc +++ b/gin/array_buffer.cc @@ -107,7 +107,7 @@ void ArrayBuffer::Private::WeakCallback( const v8::WeakCallbackData<v8::ArrayBuffer, Private>& data) { Private* parameter = data.GetParameter(); parameter->array_buffer_.Reset(); - // parameter->self_reference_.clear(); + parameter->self_reference_ = NULL; } // ArrayBuffer ---------------------------------------------------------------- diff --git a/gin/dictionary.cc b/gin/dictionary.cc index 0da85af..ca6227e 100644 --- a/gin/dictionary.cc +++ b/gin/dictionary.cc @@ -30,7 +30,8 @@ v8::Handle<v8::Value> Converter<Dictionary>::ToV8(v8::Isolate* isolate, return val.object_; } -bool FromV8(v8::Handle<v8::Value> val, Dictionary* out) { +bool Converter<Dictionary>::FromV8(v8::Handle<v8::Value> val, + Dictionary* out) { if (!val->IsObject()) return false; *out = Dictionary(out->isolate(), v8::Handle<v8::Object>::Cast(val)); diff --git a/gin/gin.gyp b/gin/gin.gyp index e0ec34c..4b08dfc 100644 --- a/gin/gin.gyp +++ b/gin/gin.gyp @@ -19,14 +19,6 @@ '../v8/tools/gyp/v8.gyp:v8', ], 'sources': [ - 'modules/console.cc', - 'modules/console.h', - 'modules/file_module_provider.cc', - 'modules/file_module_provider.h', - 'modules/module_registry.cc', - 'modules/module_registry.h', - 'modules/module_runner_delegate.cc', - 'modules/module_runner_delegate.h', 'arguments.cc', 'arguments.h', 'array_buffer.cc', @@ -39,17 +31,27 @@ 'dictionary.h', 'gin.cc', 'gin.h', + 'modules/console.cc', + 'modules/console.h', + 'modules/file_module_provider.cc', + 'modules/file_module_provider.h', + 'modules/module_registry.cc', + 'modules/module_registry.h', + 'modules/module_runner_delegate.cc', + 'modules/module_runner_delegate.h', 'per_context_data.cc', 'per_context_data.h', 'per_isolate_data.cc', 'per_isolate_data.h', + 'public/gin_embedders.h', + 'public/wrapper_info.h', 'runner.cc', 'runner.h', 'try_catch.cc', 'try_catch.h', + 'wrappable.cc', + 'wrappable.h', 'wrapper_info.cc', - 'public/gin_embedders.h', - 'public/wrapper_info.h', ], }, { @@ -100,6 +102,7 @@ 'test/run_all_unittests.cc', 'test/run_js_tests.cc', 'runner_unittest.cc', + 'wrappable_unittest.cc', ], }, ], diff --git a/gin/wrappable.cc b/gin/wrappable.cc new file mode 100644 index 0000000..980349a --- /dev/null +++ b/gin/wrappable.cc @@ -0,0 +1,63 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gin/wrappable.h" + +#include "base/logging.h" +#include "gin/per_isolate_data.h" + +namespace gin { + +Wrappable::Wrappable() { +} + +Wrappable::~Wrappable() { + wrapper_.Reset(); +} + +void Wrappable::WeakCallback( + const v8::WeakCallbackData<v8::Object, Wrappable>& data) { + Wrappable* wrappable = data.GetParameter(); + wrappable->wrapper_.Reset(); + wrappable->Release(); // Balanced in Wrappable::ConfigureWrapper. +} + +v8::Handle<v8::Object> Wrappable::CreateWrapper(v8::Isolate* isolate) { + WrapperInfo* info = GetWrapperInfo(); + PerIsolateData* data = PerIsolateData::From(isolate); + v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate(info); + CHECK(!templ.IsEmpty()); // Don't forget to register an object template. + CHECK(templ->InternalFieldCount() == kNumberOfInternalFields); + v8::Handle<v8::Object> wrapper = templ->NewInstance(); + wrapper->SetAlignedPointerInInternalField(kWrapperInfoIndex, info); + wrapper->SetAlignedPointerInInternalField(kEncodedValueIndex, this); + wrapper_.Reset(isolate, wrapper); + AddRef(); // Balanced in Wrappable::WeakCallback. + wrapper_.SetWeak(this, WeakCallback); + return wrapper; +} + +v8::Handle<v8::Value> Converter<Wrappable*>::ToV8(v8::Isolate* isolate, + Wrappable* val) { + if (val->wrapper_.IsEmpty()) + return val->CreateWrapper(isolate); + return v8::Local<v8::Object>::New(isolate, val->wrapper_); +} + +bool Converter<Wrappable*>::FromV8(v8::Handle<v8::Value> val, + Wrappable** out) { + if (!val->IsObject()) + return false; + v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(val); + WrapperInfo* info = WrapperInfo::From(obj); + if (!info) + return false; + void* pointer = obj->GetAlignedPointerFromInternalField(kEncodedValueIndex); + Wrappable* wrappable = static_cast<Wrappable*>(pointer); + CHECK(wrappable->GetWrapperInfo() == info); // Security check for cast above. + *out = wrappable; + return true; +} + +} // namespace gin diff --git a/gin/wrappable.h b/gin/wrappable.h new file mode 100644 index 0000000..66c2389 --- /dev/null +++ b/gin/wrappable.h @@ -0,0 +1,60 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef GIN_WRAPPABLE_H_ +#define GIN_WRAPPABLE_H_ + +#include "base/memory/ref_counted.h" +#include "gin/converter.h" +#include "gin/public/wrapper_info.h" + +namespace gin { + +class Wrappable : public base::RefCounted<Wrappable> { + public: + virtual WrapperInfo* GetWrapperInfo() = 0; + + protected: + Wrappable(); + virtual ~Wrappable(); + + private: + friend class base::RefCounted<Wrappable>; + friend struct Converter<Wrappable*>; + + static void WeakCallback( + const v8::WeakCallbackData<v8::Object, Wrappable>& data); + v8::Handle<v8::Object> CreateWrapper(v8::Isolate* isolate); + + v8::Persistent<v8::Object> wrapper_; // Weak + + DISALLOW_COPY_AND_ASSIGN(Wrappable); +}; + +template<> +struct Converter<Wrappable*> { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + Wrappable* val); + static bool FromV8(v8::Handle<v8::Value> val, + Wrappable** out); +}; + +template<typename T> +struct WrappableConverter { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, T* val) { + return Converter<Wrappable*>::ToV8(isolate, val); + } + static bool FromV8(v8::Handle<v8::Value> val, T** out) { + Wrappable* wrappable = 0; + if (!Converter<Wrappable*>::FromV8(val, &wrappable) + || wrappable->GetWrapperInfo() != &T::kWrapperInfo) + return false; + *out = static_cast<T*>(wrappable); + return true; + } +}; + +} // namespace gin + +#endif // GIN_WRAPPABLE_H_ diff --git a/gin/wrappable_unittest.cc b/gin/wrappable_unittest.cc new file mode 100644 index 0000000..af48769 --- /dev/null +++ b/gin/wrappable_unittest.cc @@ -0,0 +1,137 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "gin/arguments.h" +#include "gin/gin.h" +#include "gin/per_isolate_data.h" +#include "gin/test/v8_test.h" +#include "gin/wrappable.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace gin { +namespace { + +class MyObject : public Wrappable { + public: + static scoped_refptr<MyObject> Create(); + + 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() {} + + int value_; +}; + +WrapperInfo MyObject::kWrapperInfo = { kEmbedderNativeGin }; + +scoped_refptr<MyObject> MyObject::Create() { + return make_scoped_refptr(new MyObject()); +} + +WrapperInfo* MyObject::GetWrapperInfo() { + return &kWrapperInfo; +} + +} // namespace + +template<> +struct Converter<MyObject*> : public WrappableConverter<MyObject> {}; + +namespace { + +// TODO(abarth): This is too much typing. + +void MyObjectGetValue(const v8::FunctionCallbackInfo<v8::Value>& info) { + Arguments args(info); + + MyObject* obj = 0; + CHECK(args.Holder(&obj)); + + args.Return(obj->value()); +} + +void MyObjectSetValue(const v8::FunctionCallbackInfo<v8::Value>& info) { + Arguments args(info); + + MyObject* obj = 0; + CHECK(args.Holder(&obj)); + + int val = 0; + if (!args.GetNext(&val)) + return args.ThrowError(); + + obj->set_value(val); +} + +void RegisterTemplate(v8::Isolate* isolate) { + PerIsolateData* data = PerIsolateData::From(isolate); + DCHECK(data->GetObjectTemplate(&MyObject::kWrapperInfo).IsEmpty()); + + v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); + templ->SetInternalFieldCount(kNumberOfInternalFields); + templ->SetAccessorProperty( + StringToSymbol(isolate, "value"), + v8::FunctionTemplate::New(MyObjectGetValue), + v8::FunctionTemplate::New(MyObjectSetValue)); + + data->SetObjectTemplate(&MyObject::kWrapperInfo, templ); +} + +typedef V8Test WrappableTest; + +TEST_F(WrappableTest, WrapAndUnwrap) { + v8::Isolate* isolate = instance_->isolate(); + v8::HandleScope handle_scope(isolate); + + RegisterTemplate(isolate); + scoped_refptr<MyObject> obj = MyObject::Create(); + + v8::Handle<v8::Value> wrapper = ConvertToV8(isolate, obj.get()); + EXPECT_FALSE(wrapper.IsEmpty()); + + MyObject* unwrapped = 0; + EXPECT_TRUE(ConvertFromV8(wrapper, &unwrapped)); + EXPECT_EQ(obj, unwrapped); +} + +TEST_F(WrappableTest, GetAndSetProperty) { + v8::Isolate* isolate = instance_->isolate(); + v8::HandleScope handle_scope(isolate); + + RegisterTemplate(isolate); + scoped_refptr<MyObject> obj = MyObject::Create(); + + obj->set_value(42); + EXPECT_EQ(42, obj->value()); + + v8::Handle<v8::String> source = StringToV8(isolate, + "(function (obj) {" + " if (obj.value !== 42) throw 'FAIL';" + " else obj.value = 191; })"); + EXPECT_FALSE(source.IsEmpty()); + + v8::TryCatch try_catch; + v8::Handle<v8::Script> script = v8::Script::New(source); + EXPECT_FALSE(script.IsEmpty()); + v8::Handle<v8::Value> val = script->Run(); + EXPECT_FALSE(val.IsEmpty()); + v8::Handle<v8::Function> func; + EXPECT_TRUE(ConvertFromV8(val, &func)); + v8::Handle<v8::Value> argv[] = { + ConvertToV8(isolate, obj.get()), + }; + func->Call(v8::Undefined(isolate), 1, argv); + + EXPECT_EQ(191, obj->value()); +} + +} // namespace +} // namespace gin |