Commit 5c969b88 authored by jochen@chromium.org's avatar jochen@chromium.org

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
parent f209f953
......@@ -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',
......
// 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
// 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_
// 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
......@@ -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);
......
......@@ -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();
......
......@@ -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
......@@ -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);
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment