diff options
author | jochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-12 04:59:05 +0000 |
---|---|---|
committer | jochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-12 04:59:05 +0000 |
commit | 5c969b88b487d95d8e0d88c7734202dc171e5ce0 (patch) | |
tree | 926b315016dd07713a55cf09475398b4b8b7eebc | |
parent | f209f953525c13825705d29a25d0c20e7e59701f (diff) | |
download | chromium_src-5c969b88b487d95d8e0d88c7734202dc171e5ce0.zip chromium_src-5c969b88b487d95d8e0d88c7734202dc171e5ce0.tar.gz chromium_src-5c969b88b487d95d8e0d88c7734202dc171e5ce0.tar.bz2 |
gin: Add the concept of named and indexed interceptors.
This will allow for using gin as a drop-in replacement for NPObject.
BUG=347565
R=abarth@chromium.org,dcarney@chromium.org,aa@chromium.org
Review URL: https://codereview.chromium.org/194603003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@256431 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | gin/gin.gyp | 3 | ||||
-rw-r--r-- | gin/interceptor.cc | 64 | ||||
-rw-r--r-- | gin/interceptor.h | 63 | ||||
-rw-r--r-- | gin/interceptor_unittest.cc | 159 | ||||
-rw-r--r-- | gin/object_template_builder.cc | 141 | ||||
-rw-r--r-- | gin/object_template_builder.h | 2 | ||||
-rw-r--r-- | gin/per_isolate_data.cc | 51 | ||||
-rw-r--r-- | gin/per_isolate_data.h | 26 |
8 files changed, 509 insertions, 0 deletions
diff --git a/gin/gin.gyp b/gin/gin.gyp index 8221ec2..1b15dd1 100644 --- a/gin/gin.gyp +++ b/gin/gin.gyp @@ -35,6 +35,8 @@ 'function_template.h', 'gin_export.h', 'handle.h', + 'interceptor.cc', + 'interceptor.h', 'isolate_holder.cc', 'modules/console.cc', 'modules/console.h', @@ -117,6 +119,7 @@ ], 'sources': [ 'converter_unittest.cc', + 'interceptor_unittest.cc', 'modules/module_registry_unittest.cc', 'modules/timer_unittest.cc', 'per_context_data_unittest.cc', diff --git a/gin/interceptor.cc b/gin/interceptor.cc new file mode 100644 index 0000000..7efc32e --- /dev/null +++ b/gin/interceptor.cc @@ -0,0 +1,64 @@ +// Copyright 2014 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/interceptor.h" + +#include <map> + +#include "gin/per_isolate_data.h" + +namespace gin { + +NamedPropertyInterceptor::NamedPropertyInterceptor(v8::Isolate* isolate, + WrappableBase* base) + : isolate_(isolate), base_(base) { + PerIsolateData::From(isolate_)->SetNamedPropertyInterceptor(base_, this); +} + +NamedPropertyInterceptor::~NamedPropertyInterceptor() { + PerIsolateData::From(isolate_)->ClearNamedPropertyInterceptor(base_, this); +} + +v8::Local<v8::Value> NamedPropertyInterceptor::GetNamedProperty( + v8::Isolate* isolate, + const std::string& property) { + return v8::Local<v8::Value>(); +} + +void NamedPropertyInterceptor::SetNamedProperty(v8::Isolate* isolate, + const std::string& property, + v8::Local<v8::Value> value) {} + +std::vector<std::string> NamedPropertyInterceptor::EnumerateNamedProperties( + v8::Isolate* isolate) { + return std::vector<std::string>(); +} + +IndexedPropertyInterceptor::IndexedPropertyInterceptor(v8::Isolate* isolate, + WrappableBase* base) + : isolate_(isolate), base_(base) { + PerIsolateData::From(isolate_)->SetIndexedPropertyInterceptor(base_, this); +} + +IndexedPropertyInterceptor::~IndexedPropertyInterceptor() { + PerIsolateData::From(isolate_)->ClearIndexedPropertyInterceptor(base_, this); +} + +v8::Local<v8::Value> IndexedPropertyInterceptor::GetIndexedProperty( + v8::Isolate* isolate, + uint32_t index) { + return v8::Local<v8::Value>(); +} + +void IndexedPropertyInterceptor::SetIndexedProperty( + v8::Isolate* isolate, + uint32_t index, + v8::Local<v8::Value> value) {} + +std::vector<uint32_t> IndexedPropertyInterceptor::EnumerateIndexedProperties( + v8::Isolate* isolate) { + return std::vector<uint32_t>(); +} + +} // namespace gin diff --git a/gin/interceptor.h b/gin/interceptor.h new file mode 100644 index 0000000..802929b --- /dev/null +++ b/gin/interceptor.h @@ -0,0 +1,63 @@ +// Copyright 2014 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_INTERCEPTOR_H_ +#define GIN_INTERCEPTOR_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "gin/gin_export.h" +#include "v8/include/v8.h" + +namespace gin { + +class WrappableBase; + +// Base class for gin::Wrappable-derived classes that want to implement a +// property interceptor. +class GIN_EXPORT NamedPropertyInterceptor { + public: + NamedPropertyInterceptor(v8::Isolate* isolate, WrappableBase* base); + virtual ~NamedPropertyInterceptor(); + + virtual v8::Local<v8::Value> GetNamedProperty(v8::Isolate* isolate, + const std::string& property); + virtual void SetNamedProperty(v8::Isolate* isolate, + const std::string& property, + v8::Local<v8::Value> value); + virtual std::vector<std::string> EnumerateNamedProperties( + v8::Isolate* isolate); + + private: + v8::Isolate* isolate_; + WrappableBase* base_; + + DISALLOW_COPY_AND_ASSIGN(NamedPropertyInterceptor); +}; + +class GIN_EXPORT IndexedPropertyInterceptor { + public: + IndexedPropertyInterceptor(v8::Isolate* isolate, WrappableBase* base); + virtual ~IndexedPropertyInterceptor(); + + virtual v8::Local<v8::Value> GetIndexedProperty(v8::Isolate* isolate, + uint32_t index); + virtual void SetIndexedProperty(v8::Isolate* isolate, + uint32_t index, + v8::Local<v8::Value> value); + virtual std::vector<uint32_t> EnumerateIndexedProperties( + v8::Isolate* isolate); + + private: + v8::Isolate* isolate_; + WrappableBase* base_; + + DISALLOW_COPY_AND_ASSIGN(IndexedPropertyInterceptor); +}; + +} // namespace gin + +#endif // GIN_INTERCEPTOR_H_ diff --git a/gin/interceptor_unittest.cc b/gin/interceptor_unittest.cc new file mode 100644 index 0000000..ee6b7dc --- /dev/null +++ b/gin/interceptor_unittest.cc @@ -0,0 +1,159 @@ +// Copyright 2014 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/handle.h" +#include "gin/interceptor.h" +#include "gin/object_template_builder.h" +#include "gin/per_isolate_data.h" +#include "gin/public/isolate_holder.h" +#include "gin/test/v8_test.h" +#include "gin/try_catch.h" +#include "gin/wrappable.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace gin { + +class MyInterceptor : public Wrappable<MyInterceptor>, + public NamedPropertyInterceptor, + public IndexedPropertyInterceptor { + public: + static WrapperInfo kWrapperInfo; + + static gin::Handle<MyInterceptor> Create(v8::Isolate* isolate) { + return CreateHandle(isolate, new MyInterceptor(isolate)); + } + + int value() const { return value_; } + void set_value(int value) { value_ = value; } + + // gin::NamedPropertyInterceptor + virtual v8::Local<v8::Value> GetNamedProperty(v8::Isolate* isolate, + const std::string& property) + OVERRIDE { + if (property == "value") { + return ConvertToV8(isolate, value_); + } else if (property == "func") { + return CreateFunctionTemplate(isolate, + base::Bind(&MyInterceptor::Call), + HolderIsFirstArgument)->GetFunction(); + } else { + return v8::Local<v8::Value>(); + } + } + virtual void SetNamedProperty(v8::Isolate* isolate, + const std::string& property, + v8::Local<v8::Value> value) OVERRIDE { + if (property != "value") + return; + ConvertFromV8(isolate, value, &value_); + } + virtual std::vector<std::string> EnumerateNamedProperties( + v8::Isolate* isolate) OVERRIDE { + std::vector<std::string> result; + result.push_back("func"); + result.push_back("value"); + return result; + } + + // gin::IndexedPropertyInterceptor + virtual v8::Local<v8::Value> GetIndexedProperty(v8::Isolate* isolate, + uint32_t index) OVERRIDE { + if (index == 0) + return ConvertToV8(isolate, value_); + return v8::Local<v8::Value>(); + } + virtual void SetIndexedProperty(v8::Isolate* isolate, + uint32_t index, + v8::Local<v8::Value> value) OVERRIDE { + if (index != 0) + return; + ConvertFromV8(isolate, value, &value_); + } + virtual std::vector<uint32_t> EnumerateIndexedProperties(v8::Isolate* isolate) + OVERRIDE { + std::vector<uint32_t> result; + result.push_back(0); + return result; + } + + private: + explicit MyInterceptor(v8::Isolate* isolate) + : NamedPropertyInterceptor(isolate, this), + IndexedPropertyInterceptor(isolate, this), + value_(0) {} + virtual ~MyInterceptor() {} + + // gin::Wrappable + virtual ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) + OVERRIDE { + return Wrappable<MyInterceptor>::GetObjectTemplateBuilder(isolate) + .AddNamedPropertyInterceptor() + .AddIndexedPropertyInterceptor(); + } + + int Call(int value) { + int tmp = value_; + value_ = value; + return tmp; + } + + int value_; +}; + +WrapperInfo MyInterceptor::kWrapperInfo = {kEmbedderNativeGin}; + +class InterceptorTest : public V8Test { + public: + void RunInterceptorTest(const std::string& script_source) { + v8::Isolate* isolate = instance_->isolate(); + v8::HandleScope handle_scope(isolate); + + gin::Handle<MyInterceptor> obj = MyInterceptor::Create(isolate); + + obj->set_value(42); + EXPECT_EQ(42, obj->value()); + + v8::Handle<v8::String> source = StringToV8(isolate, script_source); + EXPECT_FALSE(source.IsEmpty()); + + gin::TryCatch try_catch; + v8::Handle<v8::Script> script = v8::Script::Compile(source); + EXPECT_FALSE(script.IsEmpty()); + v8::Handle<v8::Value> val = script->Run(); + EXPECT_FALSE(val.IsEmpty()); + v8::Handle<v8::Function> func; + EXPECT_TRUE(ConvertFromV8(isolate, val, &func)); + v8::Handle<v8::Value> argv[] = {ConvertToV8(isolate, obj.get()), }; + func->Call(v8::Undefined(isolate), 1, argv); + EXPECT_FALSE(try_catch.HasCaught()); + EXPECT_EQ("", try_catch.GetStackTrace()); + + EXPECT_EQ(191, obj->value()); + } +}; + +TEST_F(InterceptorTest, NamedInterceptor) { + RunInterceptorTest( + "(function (obj) {" + " if (obj.value !== 42) throw 'FAIL';" + " else obj.value = 191; })"); +} + +TEST_F(InterceptorTest, NamedInterceptorCall) { + RunInterceptorTest( + "(function (obj) {" + " if (obj.func(191) !== 42) throw 'FAIL';" + " })"); +} + +TEST_F(InterceptorTest, IndexedInterceptor) { + RunInterceptorTest( + "(function (obj) {" + " if (obj[0] !== 42) throw 'FAIL';" + " else obj[0] = 191; })"); +} + +} // namespace gin diff --git a/gin/object_template_builder.cc b/gin/object_template_builder.cc index 8bc2714..603166c 100644 --- a/gin/object_template_builder.cc +++ b/gin/object_template_builder.cc @@ -3,10 +3,133 @@ // found in the LICENSE file. #include "gin/object_template_builder.h" + +#include "gin/interceptor.h" +#include "gin/per_isolate_data.h" #include "gin/public/wrapper_info.h" namespace gin { +namespace { + +WrappableBase* WrappableFromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val) { + 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. + if (!info) + return NULL; + + // We don't further validate the type of the object, but assume it's derived + // from WrappableBase. We look up the pointer in a global registry, to make + // sure it's actually pointed to a valid life object. + return static_cast<WrappableBase*>( + obj->GetAlignedPointerFromInternalField(kEncodedValueIndex)); +} + +NamedPropertyInterceptor* NamedInterceptorFromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val) { + WrappableBase* base = WrappableFromV8(isolate, val); + if (!base) + return NULL; + return PerIsolateData::From(isolate)->GetNamedPropertyInterceptor(base); +} + +IndexedPropertyInterceptor* IndexedInterceptorFromV8( + v8::Isolate* isolate, + v8::Handle<v8::Value> val) { + WrappableBase* base = WrappableFromV8(isolate, val); + if (!base) + return NULL; + return PerIsolateData::From(isolate)->GetIndexedPropertyInterceptor(base); +} + +void NamedPropertyGetter(v8::Local<v8::String> property, + const v8::PropertyCallbackInfo<v8::Value>& info) { + v8::Isolate* isolate = info.GetIsolate(); + NamedPropertyInterceptor* interceptor = + NamedInterceptorFromV8(isolate, info.Holder()); + if (!interceptor) + return; + std::string name; + ConvertFromV8(isolate, property, &name); + info.GetReturnValue().Set(interceptor->GetNamedProperty(isolate, name)); +} + +void NamedPropertySetter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::PropertyCallbackInfo<v8::Value>& info) { + v8::Isolate* isolate = info.GetIsolate(); + NamedPropertyInterceptor* interceptor = + NamedInterceptorFromV8(isolate, info.Holder()); + if (!interceptor) + return; + std::string name; + ConvertFromV8(isolate, property, &name); + interceptor->SetNamedProperty(isolate, name, value); +} + +void NamedPropertyQuery(v8::Local<v8::String> property, + const v8::PropertyCallbackInfo<v8::Integer>& info) { + v8::Isolate* isolate = info.GetIsolate(); + NamedPropertyInterceptor* interceptor = + NamedInterceptorFromV8(isolate, info.Holder()); + if (!interceptor) + return; + std::string name; + ConvertFromV8(isolate, property, &name); + if (interceptor->GetNamedProperty(isolate, name).IsEmpty()) + return; + info.GetReturnValue().Set(0); +} + +void NamedPropertyEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info) { + v8::Isolate* isolate = info.GetIsolate(); + NamedPropertyInterceptor* interceptor = + NamedInterceptorFromV8(isolate, info.Holder()); + if (!interceptor) + return; + info.GetReturnValue().Set(v8::Handle<v8::Array>::Cast( + ConvertToV8(isolate, interceptor->EnumerateNamedProperties(isolate)))); +} + +void IndexedPropertyGetter(uint32_t index, + const v8::PropertyCallbackInfo<v8::Value>& info) { + v8::Isolate* isolate = info.GetIsolate(); + IndexedPropertyInterceptor* interceptor = + IndexedInterceptorFromV8(isolate, info.Holder()); + if (!interceptor) + return; + info.GetReturnValue().Set(interceptor->GetIndexedProperty(isolate, index)); +} + +void IndexedPropertySetter(uint32_t index, + v8::Local<v8::Value> value, + const v8::PropertyCallbackInfo<v8::Value>& info) { + v8::Isolate* isolate = info.GetIsolate(); + IndexedPropertyInterceptor* interceptor = + IndexedInterceptorFromV8(isolate, info.Holder()); + if (!interceptor) + return; + interceptor->SetIndexedProperty(isolate, index, value); +} + +void IndexedPropertyEnumerator( + const v8::PropertyCallbackInfo<v8::Array>& info) { + v8::Isolate* isolate = info.GetIsolate(); + IndexedPropertyInterceptor* interceptor = + IndexedInterceptorFromV8(isolate, info.Holder()); + if (!interceptor) + return; + info.GetReturnValue().Set(v8::Handle<v8::Array>::Cast( + ConvertToV8(isolate, interceptor->EnumerateIndexedProperties(isolate)))); +} + +} // namespace + ObjectTemplateBuilder::ObjectTemplateBuilder(v8::Isolate* isolate) : isolate_(isolate), template_(v8::ObjectTemplate::New(isolate)) { template_->SetInternalFieldCount(kNumberOfInternalFields); @@ -15,6 +138,24 @@ ObjectTemplateBuilder::ObjectTemplateBuilder(v8::Isolate* isolate) ObjectTemplateBuilder::~ObjectTemplateBuilder() { } +ObjectTemplateBuilder& ObjectTemplateBuilder::AddNamedPropertyInterceptor() { + template_->SetNamedPropertyHandler(&NamedPropertyGetter, + &NamedPropertySetter, + &NamedPropertyQuery, + NULL, + &NamedPropertyEnumerator); + return *this; +} + +ObjectTemplateBuilder& ObjectTemplateBuilder::AddIndexedPropertyInterceptor() { + template_->SetIndexedPropertyHandler(&IndexedPropertyGetter, + &IndexedPropertySetter, + NULL, + NULL, + &IndexedPropertyEnumerator); + return *this; +} + ObjectTemplateBuilder& ObjectTemplateBuilder::SetImpl( const base::StringPiece& name, v8::Handle<v8::Data> val) { template_->Set(StringToSymbol(isolate_, name), val); diff --git a/gin/object_template_builder.h b/gin/object_template_builder.h index 768f4c8..3d025a9 100644 --- a/gin/object_template_builder.h +++ b/gin/object_template_builder.h @@ -124,6 +124,8 @@ class GIN_EXPORT ObjectTemplateBuilder { CallbackTraits<T>::SetAsFunctionHandler(isolate_, template_, callback); return *this; } + ObjectTemplateBuilder& AddNamedPropertyInterceptor(); + ObjectTemplateBuilder& AddIndexedPropertyInterceptor(); v8::Local<v8::ObjectTemplate> Build(); diff --git a/gin/per_isolate_data.cc b/gin/per_isolate_data.cc index 7de9047..b3f24ab 100644 --- a/gin/per_isolate_data.cc +++ b/gin/per_isolate_data.cc @@ -2,6 +2,7 @@ // 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/per_isolate_data.h" #include "gin/public/gin_embedders.h" @@ -55,4 +56,54 @@ v8::Local<v8::FunctionTemplate> PerIsolateData::GetFunctionTemplate( return it->second.Get(isolate_); } +void PerIsolateData::SetIndexedPropertyInterceptor( + WrappableBase* base, + IndexedPropertyInterceptor* interceptor) { + indexed_interceptors_[base] = interceptor; +} + +void PerIsolateData::SetNamedPropertyInterceptor( + WrappableBase* base, + NamedPropertyInterceptor* interceptor) { + named_interceptors_[base] = interceptor; +} + +void PerIsolateData::ClearIndexedPropertyInterceptor( + WrappableBase* base, + IndexedPropertyInterceptor* interceptor) { + IndexedPropertyInterceptorMap::iterator it = indexed_interceptors_.find(base); + if (it != indexed_interceptors_.end()) + indexed_interceptors_.erase(it); + else + NOTREACHED(); +} + +void PerIsolateData::ClearNamedPropertyInterceptor( + WrappableBase* base, + NamedPropertyInterceptor* interceptor) { + NamedPropertyInterceptorMap::iterator it = named_interceptors_.find(base); + if (it != named_interceptors_.end()) + named_interceptors_.erase(it); + else + NOTREACHED(); +} + +IndexedPropertyInterceptor* PerIsolateData::GetIndexedPropertyInterceptor( + WrappableBase* base) { + IndexedPropertyInterceptorMap::iterator it = indexed_interceptors_.find(base); + if (it != indexed_interceptors_.end()) + return it->second; + else + return NULL; +} + +NamedPropertyInterceptor* PerIsolateData::GetNamedPropertyInterceptor( + WrappableBase* base) { + NamedPropertyInterceptorMap::iterator it = named_interceptors_.find(base); + if (it != named_interceptors_.end()) + return it->second; + else + return NULL; +} + } // namespace gin diff --git a/gin/per_isolate_data.h b/gin/per_isolate_data.h index 18c230b..fbdbca7 100644 --- a/gin/per_isolate_data.h +++ b/gin/per_isolate_data.h @@ -14,6 +14,10 @@ namespace gin { +class IndexedPropertyInterceptor; +class NamedPropertyInterceptor; +class WrappableBase; + // There is one instance of PerIsolateData per v8::Isolate managed by Gin. This // class stores all the Gin-related data that varies per isolate. class GIN_EXPORT PerIsolateData { @@ -38,6 +42,22 @@ class GIN_EXPORT PerIsolateData { v8::Local<v8::ObjectTemplate> GetObjectTemplate(WrapperInfo* info); v8::Local<v8::FunctionTemplate> GetFunctionTemplate(WrapperInfo* info); + // We maintain a map from Wrappable objects that derive from one of the + // interceptor interfaces to the interceptor interface pointers. + void SetIndexedPropertyInterceptor(WrappableBase* base, + IndexedPropertyInterceptor* interceptor); + void SetNamedPropertyInterceptor(WrappableBase* base, + NamedPropertyInterceptor* interceptor); + + void ClearIndexedPropertyInterceptor(WrappableBase* base, + IndexedPropertyInterceptor* interceptor); + void ClearNamedPropertyInterceptor(WrappableBase* base, + NamedPropertyInterceptor* interceptor); + + IndexedPropertyInterceptor* GetIndexedPropertyInterceptor( + WrappableBase* base); + NamedPropertyInterceptor* GetNamedPropertyInterceptor(WrappableBase* base); + v8::Isolate* isolate() { return isolate_; } v8::ArrayBuffer::Allocator* allocator() { return allocator_; } @@ -46,6 +66,10 @@ class GIN_EXPORT PerIsolateData { WrapperInfo*, v8::Eternal<v8::ObjectTemplate> > ObjectTemplateMap; typedef std::map< WrapperInfo*, v8::Eternal<v8::FunctionTemplate> > FunctionTemplateMap; + typedef std::map<WrappableBase*, IndexedPropertyInterceptor*> + IndexedPropertyInterceptorMap; + typedef std::map<WrappableBase*, NamedPropertyInterceptor*> + NamedPropertyInterceptorMap; // PerIsolateData doesn't actually own |isolate_|. Instead, the isolate is // owned by the IsolateHolder, which also owns the PerIsolateData. @@ -53,6 +77,8 @@ class GIN_EXPORT PerIsolateData { v8::ArrayBuffer::Allocator* allocator_; ObjectTemplateMap object_templates_; FunctionTemplateMap function_templates_; + IndexedPropertyInterceptorMap indexed_interceptors_; + NamedPropertyInterceptorMap named_interceptors_; DISALLOW_COPY_AND_ASSIGN(PerIsolateData); }; |