diff options
author | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-08 14:30:41 +0200 |
---|---|---|
committer | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-12 13:49:54 +0200 |
commit | ab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch) | |
tree | 498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/gin | |
parent | 4ce69f7403811819800e7c5ae1318b2647e778d1 (diff) |
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca
Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/gin')
50 files changed, 2191 insertions, 448 deletions
diff --git a/chromium/gin/BUILD.gn b/chromium/gin/BUILD.gn new file mode 100644 index 00000000000..7b3f06edf5d --- /dev/null +++ b/chromium/gin/BUILD.gn @@ -0,0 +1,125 @@ +# 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. + +component("gin") { + sources = [ + "arguments.cc", + "arguments.h", + "array_buffer.cc", + "array_buffer.h", + "context_holder.cc", + "converter.cc", + "converter.h", + "dictionary.cc", + "dictionary.h", + "function_template.cc", + "function_template.h", + "gin_export.h", + "handle.h", + "interceptor.cc", + "interceptor.h", + "isolate_holder.cc", + "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_registry_observer.h", + "modules/module_runner_delegate.cc", + "modules/module_runner_delegate.h", + "modules/timer.cc", + "modules/timer.h", + "object_template_builder.cc", + "object_template_builder.h", + "per_context_data.cc", + "per_context_data.h", + "per_isolate_data.cc", + "per_isolate_data.h", + "public/context_holder.h", + "public/gin_embedders.h", + "public/isolate_holder.h", + "public/v8_platform.h", + "public/wrapper_info.h", + "runner.cc", + "runner.h", + "shell_runner.cc", + "shell_runner.h", + "try_catch.cc", + "try_catch.h", + "v8_platform.cc", + "wrappable.cc", + "wrappable.h", + "wrapper_info.cc", + ] + + defines = [ "GIN_IMPLEMENTATION" ] + deps = [ + "//base", + "//base/third_party/dynamic_annotations", + "//v8", + ] + + forward_dependent_configs_from = [ + "//base", + "//v8", + ] +} + +executable("gin_shell") { + sources = [ + "shell/gin_main.cc", + ] + + deps = [ + ":gin", + "//base", + "//base:i18n", + "//v8", + ] +} + +source_set("gin_test") { + sources = [ + "test/file_runner.cc", + "test/file_runner.h", + "test/gc.cc", + "test/gc.h", + "test/gtest.cc", + "test/gtest.h", + "test/v8_test.cc", + "test/v8_test.h", + ] + + deps = [ + ":gin", + "//testing/gtest", + "//v8", + ] + + forward_dependent_configs_from = [ + ":gin", + "//testing/gtest", + ] +} + +test("gin_unittests") { + sources = [ + "converter_unittest.cc", + "interceptor_unittest.cc", + "modules/module_registry_unittest.cc", + "modules/timer_unittest.cc", + "per_context_data_unittest.cc", + "shell_runner_unittest.cc", + "test/run_all_unittests.cc", + "test/run_js_tests.cc", + "wrappable_unittest.cc", + ] + + deps = [ + ":gin_test", + "//base/test:test_support", + "//v8", + ] +} diff --git a/chromium/gin/README b/chromium/gin/README index 82d06188f3f..fc2d92e0f78 100644 --- a/chromium/gin/README +++ b/chromium/gin/README @@ -1,8 +1,24 @@ Gin - Lightweight bindings for V8 ================================= -This directory contains gin, a lightweight bindings library for V8. These -bindings are not compatible with the V8 bindings used by Blink because both -want to control the v8::Isolate's internal data field. Maybe in some future -world we'll refactor the Blink V8 bindings to use this system. In the meantime, -these bindings are convenient for projects other than Blink that use V8. +This directory contains Gin, a set of utilities to make working with V8 easier. + +Here are some of the key bits: + +* converter.h: Templatized JS<->C++ conversion routines for many common C++ + types. You can define your own by specializing Converter. + +* function_template.h: Create JavaScript functions that dispatch to any C++ + function, member function pointer, or base::Callback. + +* object_template_builder.h: A handy utility for creation of v8::ObjectTemplate. + +* wrappable.h: Base class for C++ classes that want to be owned by the V8 GC. + Wrappable objects are automatically deleted when GC discovers that nothing in + the V8 heap refers to them. This is also an easy way to expose C++ objects to + JavaScript. + +* runner.h: Create script contexts and run code in them. + +* module_runner_delegate.h: A delegate for runner that implements a subset of + the AMD module specification. Also see modules/ with some example modules. diff --git a/chromium/gin/arguments.h b/chromium/gin/arguments.h index ec4ae80e882..509c22ca14f 100644 --- a/chromium/gin/arguments.h +++ b/chromium/gin/arguments.h @@ -56,6 +56,17 @@ class GIN_EXPORT Arguments { return true; } + bool Skip() { + if (next_ >= info_->Length()) + return false; + next_++; + return true; + } + + int Length() const { + return info_->Length(); + } + template<typename T> void Return(T val) { info_->GetReturnValue().Set(ConvertToV8(isolate_, val)); diff --git a/chromium/gin/array_buffer.cc b/chromium/gin/array_buffer.cc index ee9f2a5867b..b777402e644 100644 --- a/chromium/gin/array_buffer.cc +++ b/chromium/gin/array_buffer.cc @@ -2,17 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "gin/array_buffer.h" - #include <stdlib.h> +#include "base/logging.h" +#include "gin/array_buffer.h" +#include "gin/per_isolate_data.h" + namespace gin { +namespace { + +gin::WrapperInfo g_array_buffer_wrapper_info = {gin::kEmbedderNativeGin}; + +} // namespace + COMPILE_ASSERT(V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT == 2, array_buffers_must_have_two_internal_fields); -static const int kBufferViewPrivateIndex = 0; - // ArrayBufferAllocator ------------------------------------------------------- void* ArrayBufferAllocator::Allocate(size_t length) { @@ -72,6 +78,7 @@ class ArrayBuffer::Private : public base::RefCounted<ArrayBuffer::Private> { v8::Persistent<v8::ArrayBuffer> array_buffer_; scoped_refptr<Private> self_reference_; + v8::Isolate* isolate_; void* buffer_; size_t length_; }; @@ -79,28 +86,34 @@ class ArrayBuffer::Private : public base::RefCounted<ArrayBuffer::Private> { scoped_refptr<ArrayBuffer::Private> ArrayBuffer::Private::From( v8::Isolate* isolate, v8::Handle<v8::ArrayBuffer> array) { if (array->IsExternal()) { + CHECK_EQ(WrapperInfo::From(v8::Handle<v8::Object>::Cast(array)), + &g_array_buffer_wrapper_info) + << "Cannot mix blink and gin ArrayBuffers"; return make_scoped_refptr(static_cast<Private*>( - array->GetAlignedPointerFromInternalField(kBufferViewPrivateIndex))); + array->GetAlignedPointerFromInternalField(kEncodedValueIndex))); } return make_scoped_refptr(new Private(isolate, array)); } ArrayBuffer::Private::Private(v8::Isolate* isolate, v8::Handle<v8::ArrayBuffer> array) - : array_buffer_(isolate, array) { + : array_buffer_(isolate, array), isolate_(isolate) { // Take ownership of the array buffer. + CHECK(!array->IsExternal()); v8::ArrayBuffer::Contents contents = array->Externalize(); buffer_ = contents.Data(); length_ = contents.ByteLength(); - array->SetAlignedPointerInInternalField(kBufferViewPrivateIndex, this); + array->SetAlignedPointerInInternalField(kWrapperInfoIndex, + &g_array_buffer_wrapper_info); + array->SetAlignedPointerInInternalField(kEncodedValueIndex, this); self_reference_ = this; // Cleared in WeakCallback. array_buffer_.SetWeak(this, WeakCallback); } ArrayBuffer::Private::~Private() { - ArrayBufferAllocator::SharedInstance()->Free(buffer_, length_); + PerIsolateData::From(isolate_)->allocator()->Free(buffer_, length_); } void ArrayBuffer::Private::WeakCallback( @@ -162,6 +175,14 @@ ArrayBufferView::ArrayBufferView(v8::Isolate* isolate, ArrayBufferView::~ArrayBufferView() { } +ArrayBufferView& ArrayBufferView::operator=(const ArrayBufferView& other) { + array_buffer_ = other.array_buffer_; + offset_ = other.offset_; + num_bytes_ = other.num_bytes_; + return *this; +} + + // Converter<ArrayBufferView> ------------------------------------------------- bool Converter<ArrayBufferView>::FromV8(v8::Isolate* isolate, diff --git a/chromium/gin/array_buffer.h b/chromium/gin/array_buffer.h index 7886fa9be05..858cbf1088a 100644 --- a/chromium/gin/array_buffer.h +++ b/chromium/gin/array_buffer.h @@ -26,7 +26,6 @@ class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { class GIN_EXPORT ArrayBuffer { public: ArrayBuffer(); - explicit ArrayBuffer(v8::Isolate* isolate); ArrayBuffer(v8::Isolate* isolate, v8::Handle<v8::ArrayBuffer> buffer); ~ArrayBuffer(); ArrayBuffer& operator=(const ArrayBuffer& other); @@ -55,6 +54,7 @@ class GIN_EXPORT ArrayBufferView { ArrayBufferView(); ArrayBufferView(v8::Isolate* isolate, v8::Handle<v8::ArrayBufferView> view); ~ArrayBufferView(); + ArrayBufferView& operator=(const ArrayBufferView& other); void* bytes() const { return static_cast<uint8_t*>(array_buffer_.bytes()) + offset_; diff --git a/chromium/gin/context_holder.cc b/chromium/gin/context_holder.cc index 32b50518376..241b25622c5 100644 --- a/chromium/gin/context_holder.cc +++ b/chromium/gin/context_holder.cc @@ -14,20 +14,14 @@ ContextHolder::ContextHolder(v8::Isolate* isolate) } ContextHolder::~ContextHolder() { - v8::HandleScope handle_scope(isolate()); - v8::Handle<v8::Context> context = this->context(); - - data_->Detach(context); + // PerContextData needs to be destroyed before the context. data_.reset(); - - // TODO(abarth): Figure out how to set kResetInDestructor to true. - context_.Reset(); } void ContextHolder::SetContext(v8::Handle<v8::Context> context) { DCHECK(context_.IsEmpty()); context_.Reset(isolate_, context); - data_.reset(new PerContextData(context)); + data_.reset(new PerContextData(this, context)); } } // namespace gin diff --git a/chromium/gin/converter.cc b/chromium/gin/converter.cc index 54736f95aaf..07437b7edc2 100644 --- a/chromium/gin/converter.cc +++ b/chromium/gin/converter.cc @@ -79,6 +79,18 @@ bool Converter<uint64_t>::FromV8(Isolate* isolate, Handle<Value> val, return true; } +Handle<Value> Converter<float>::ToV8(Isolate* isolate, float val) { + return Number::New(isolate, val).As<Value>(); +} + +bool Converter<float>::FromV8(Isolate* isolate, Handle<Value> val, + float* out) { + if (!val->IsNumber()) + return false; + *out = static_cast<float>(val->NumberValue()); + return true; +} + Handle<Value> Converter<double>::ToV8(Isolate* isolate, double val) { return Number::New(isolate, val).As<Value>(); } diff --git a/chromium/gin/converter.h b/chromium/gin/converter.h index ee029a7f770..e5c95fc1f9f 100644 --- a/chromium/gin/converter.h +++ b/chromium/gin/converter.h @@ -65,6 +65,15 @@ struct GIN_EXPORT Converter<uint64_t> { }; template<> +struct GIN_EXPORT Converter<float> { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + float val); + static bool FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + float* out); +}; + +template<> struct GIN_EXPORT Converter<double> { static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, double val); @@ -167,8 +176,7 @@ struct Converter<std::vector<T> > { // Convenience functions that deduce T. template<typename T> -v8::Handle<v8::Value> ConvertToV8(v8::Isolate* isolate, - T input) { +v8::Handle<v8::Value> ConvertToV8(v8::Isolate* isolate, T input) { return Converter<T>::ToV8(isolate, input); } diff --git a/chromium/gin/converter_unittest.cc b/chromium/gin/converter_unittest.cc index 9b831b91ac0..791d7e66ec5 100644 --- a/chromium/gin/converter_unittest.cc +++ b/chromium/gin/converter_unittest.cc @@ -73,8 +73,9 @@ TEST_F(ConverterTest, Int32) { int test_data_to[] = {-1, 0, 1}; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data_to); ++i) { - EXPECT_TRUE(Converter<int32_t>::ToV8(instance_->isolate(), - test_data_to[i])->StrictEquals(Integer::New(test_data_to[i]))); + EXPECT_TRUE(Converter<int32_t>::ToV8(instance_->isolate(), test_data_to[i]) + ->StrictEquals( + Integer::New(instance_->isolate(), test_data_to[i]))); } struct { diff --git a/chromium/gin/function_template.cc b/chromium/gin/function_template.cc index 5dd5ecd3d50..f76a05bea6b 100644 --- a/chromium/gin/function_template.cc +++ b/chromium/gin/function_template.cc @@ -4,23 +4,30 @@ #include "gin/function_template.h" -#include "gin/per_isolate_data.h" - namespace gin { -WrapperInfo internal::CallbackHolderBase::kWrapperInfo = { kEmbedderNativeGin }; +namespace internal { + +CallbackHolderBase::CallbackHolderBase(v8::Isolate* isolate) + : v8_ref_(isolate, v8::External::New(isolate, this)) { + v8_ref_.SetWeak(this, &CallbackHolderBase::WeakCallback); +} -void InitFunctionTemplates(PerIsolateData* isolate_data) { - if (!isolate_data->GetObjectTemplate( - &internal::CallbackHolderBase::kWrapperInfo).IsEmpty()) { - return; - } +CallbackHolderBase::~CallbackHolderBase() { + DCHECK(v8_ref_.IsEmpty()); +} - v8::Handle<v8::ObjectTemplate> templ( - v8::ObjectTemplate::New(isolate_data->isolate())); - templ->SetInternalFieldCount(kNumberOfInternalFields); - isolate_data->SetObjectTemplate(&internal::CallbackHolderBase::kWrapperInfo, - templ); +v8::Handle<v8::External> CallbackHolderBase::GetHandle(v8::Isolate* isolate) { + return v8::Local<v8::External>::New(isolate, v8_ref_); } +// static +void CallbackHolderBase::WeakCallback( + const v8::WeakCallbackData<v8::External, CallbackHolderBase>& data) { + data.GetParameter()->v8_ref_.Reset(); + delete data.GetParameter(); +} + +} // namespace internal + } // namespace gin diff --git a/chromium/gin/function_template.h b/chromium/gin/function_template.h index 14f6e78fe2f..7ba54b5910e 100644 --- a/chromium/gin/function_template.h +++ b/chromium/gin/function_template.h @@ -16,11 +16,6 @@ #include "gin/arguments.h" #include "gin/converter.h" #include "gin/gin_export.h" -#include "gin/handle.h" -#include "gin/public/gin_embedders.h" -#include "gin/public/wrapper_info.h" -#include "gin/wrappable.h" - #include "v8/include/v8.h" namespace gin { @@ -50,31 +45,39 @@ struct CallbackParamTraits<const T*> { // CallbackHolder and CallbackHolderBase are used to pass a base::Callback from // CreateFunctionTemplate through v8 (via v8::FunctionTemplate) to // DispatchToCallback, where it is invoked. -// -// v8::FunctionTemplate only supports passing void* as data so how do we know -// when to delete the base::Callback? That's where CallbackHolderBase comes in. -// 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. // This simple base class is used so that we can share a single object template // among every CallbackHolder instance. -class GIN_EXPORT CallbackHolderBase : public Wrappable<CallbackHolderBase> { +class GIN_EXPORT CallbackHolderBase { public: - static WrapperInfo kWrapperInfo; + v8::Handle<v8::External> GetHandle(v8::Isolate* isolate); + protected: - virtual ~CallbackHolderBase() {} + explicit CallbackHolderBase(v8::Isolate* isolate); + virtual ~CallbackHolderBase(); + + private: + static void WeakCallback( + const v8::WeakCallbackData<v8::External, CallbackHolderBase>& data); + + v8::Persistent<v8::External> v8_ref_; + + DISALLOW_COPY_AND_ASSIGN(CallbackHolderBase); }; template<typename Sig> class CallbackHolder : public CallbackHolderBase { public: - CallbackHolder(const base::Callback<Sig>& callback, int flags) - : callback(callback), flags(flags) {} + CallbackHolder(v8::Isolate* isolate, + const base::Callback<Sig>& callback, + int flags) + : CallbackHolderBase(isolate), callback(callback), flags(flags) {} base::Callback<Sig> callback; int flags; private: virtual ~CallbackHolder() {} + + DISALLOW_COPY_AND_ASSIGN(CallbackHolder); }; @@ -86,10 +89,69 @@ class CallbackHolder : public CallbackHolderBase { // expression to foo. As a result, we must specialize the case of Callbacks that // have the void return type. template<typename R, typename P1 = void, typename P2 = void, - typename P3 = void, typename P4 = void> + typename P3 = void, typename P4 = void, typename P5 = void, + typename P6 = void> struct Invoker { inline static void Go( Arguments* args, + const base::Callback<R(P1, P2, P3, P4, P5, P6)>& callback, + const P1& a1, + const P2& a2, + const P3& a3, + const P4& a4, + const P5& a5, + const P6& a6) { + args->Return(callback.Run(a1, a2, a3, a4, a5, a6)); + } +}; +template<typename P1, typename P2, typename P3, typename P4, typename P5, + typename P6> +struct Invoker<void, P1, P2, P3, P4, P5, P6> { + inline static void Go( + Arguments* args, + const base::Callback<void(P1, P2, P3, P4, P5, P6)>& callback, + const P1& a1, + const P2& a2, + const P3& a3, + const P4& a4, + const P5& a5, + const P6& a6) { + callback.Run(a1, a2, a3, a4, a5, a6); + } +}; + +template<typename R, typename P1, typename P2, typename P3, typename P4, + typename P5> +struct Invoker<R, P1, P2, P3, P4, P5, void> { + inline static void Go( + Arguments* args, + const base::Callback<R(P1, P2, P3, P4, P5)>& callback, + const P1& a1, + const P2& a2, + const P3& a3, + const P4& a4, + const P5& a5) { + args->Return(callback.Run(a1, a2, a3, a4, a5)); + } +}; +template<typename P1, typename P2, typename P3, typename P4, typename P5> +struct Invoker<void, P1, P2, P3, P4, P5, void> { + inline static void Go( + Arguments* args, + const base::Callback<void(P1, P2, P3, P4, P5)>& callback, + const P1& a1, + const P2& a2, + const P3& a3, + const P4& a4, + const P5& a5) { + callback.Run(a1, a2, a3, a4, a5); + } +}; + +template<typename R, typename P1, typename P2, typename P3, typename P4> +struct Invoker<R, P1, P2, P3, P4, void, void> { + inline static void Go( + Arguments* args, const base::Callback<R(P1, P2, P3, P4)>& callback, const P1& a1, const P2& a2, @@ -99,7 +161,7 @@ struct Invoker { } }; template<typename P1, typename P2, typename P3, typename P4> -struct Invoker<void, P1, P2, P3, P4> { +struct Invoker<void, P1, P2, P3, P4, void, void> { inline static void Go( Arguments* args, const base::Callback<void(P1, P2, P3, P4)>& callback, @@ -112,7 +174,7 @@ struct Invoker<void, P1, P2, P3, P4> { }; template<typename R, typename P1, typename P2, typename P3> -struct Invoker<R, P1, P2, P3, void> { +struct Invoker<R, P1, P2, P3, void, void, void> { inline static void Go( Arguments* args, const base::Callback<R(P1, P2, P3)>& callback, @@ -123,7 +185,7 @@ struct Invoker<R, P1, P2, P3, void> { } }; template<typename P1, typename P2, typename P3> -struct Invoker<void, P1, P2, P3, void> { +struct Invoker<void, P1, P2, P3, void, void, void> { inline static void Go( Arguments* args, const base::Callback<void(P1, P2, P3)>& callback, @@ -135,7 +197,7 @@ struct Invoker<void, P1, P2, P3, void> { }; template<typename R, typename P1, typename P2> -struct Invoker<R, P1, P2, void, void> { +struct Invoker<R, P1, P2, void, void, void, void> { inline static void Go( Arguments* args, const base::Callback<R(P1, P2)>& callback, @@ -145,7 +207,7 @@ struct Invoker<R, P1, P2, void, void> { } }; template<typename P1, typename P2> -struct Invoker<void, P1, P2, void, void> { +struct Invoker<void, P1, P2, void, void, void, void> { inline static void Go( Arguments* args, const base::Callback<void(P1, P2)>& callback, @@ -156,7 +218,7 @@ struct Invoker<void, P1, P2, void, void> { }; template<typename R, typename P1> -struct Invoker<R, P1, void, void, void> { +struct Invoker<R, P1, void, void, void, void, void> { inline static void Go( Arguments* args, const base::Callback<R(P1)>& callback, @@ -165,7 +227,7 @@ struct Invoker<R, P1, void, void, void> { } }; template<typename P1> -struct Invoker<void, P1, void, void, void> { +struct Invoker<void, P1, void, void, void, void, void> { inline static void Go( Arguments* args, const base::Callback<void(P1)>& callback, @@ -175,7 +237,7 @@ struct Invoker<void, P1, void, void, void> { }; template<typename R> -struct Invoker<R, void, void, void, void> { +struct Invoker<R, void, void, void, void, void, void> { inline static void Go( Arguments* args, const base::Callback<R()>& callback) { @@ -183,7 +245,7 @@ struct Invoker<R, void, void, void, void> { } }; template<> -struct Invoker<void, void, void, void, void> { +struct Invoker<void, void, void, void, void, void, void> { inline static void Go( Arguments* args, const base::Callback<void()>& callback) { @@ -209,6 +271,18 @@ inline bool GetNextArgument(Arguments* args, int create_flags, bool is_first, *result = *args; return true; } +inline bool GetNextArgument(Arguments* args, int create_flags, bool is_first, + Arguments** result) { + *result = args; + return true; +} + +// It's common for clients to just need the isolate, so we make that easy. +inline bool GetNextArgument(Arguments* args, int create_flags, + bool is_first, v8::Isolate** result) { + *result = args->isolate(); + return true; +} // DispatchToCallback converts all the JavaScript arguments to C++ types and @@ -222,8 +296,10 @@ struct Dispatcher<R()> { static void DispatchToCallback( const v8::FunctionCallbackInfo<v8::Value>& info) { Arguments args(info); - CallbackHolderBase* holder_base = NULL; - CHECK(args.GetData(&holder_base)); + v8::Handle<v8::External> v8_holder; + CHECK(args.GetData(&v8_holder)); + CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>( + v8_holder->Value()); typedef CallbackHolder<R()> HolderT; HolderT* holder = static_cast<HolderT*>(holder_base); @@ -237,8 +313,10 @@ 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)); + v8::Handle<v8::External> v8_holder; + CHECK(args.GetData(&v8_holder)); + CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>( + v8_holder->Value()); typedef CallbackHolder<R(P1)> HolderT; HolderT* holder = static_cast<HolderT*>(holder_base); @@ -258,8 +336,10 @@ 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)); + v8::Handle<v8::External> v8_holder; + CHECK(args.GetData(&v8_holder)); + CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>( + v8_holder->Value()); typedef CallbackHolder<R(P1, P2)> HolderT; HolderT* holder = static_cast<HolderT*>(holder_base); @@ -281,8 +361,10 @@ 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)); + v8::Handle<v8::External> v8_holder; + CHECK(args.GetData(&v8_holder)); + CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>( + v8_holder->Value()); typedef CallbackHolder<R(P1, P2, P3)> HolderT; HolderT* holder = static_cast<HolderT*>(holder_base); @@ -306,8 +388,10 @@ 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)); + v8::Handle<v8::External> v8_holder; + CHECK(args.GetData(&v8_holder)); + CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>( + v8_holder->Value()); typedef CallbackHolder<R(P1, P2, P3, P4)> HolderT; HolderT* holder = static_cast<HolderT*>(holder_base); @@ -328,12 +412,75 @@ struct Dispatcher<R(P1, P2, P3, P4)> { } }; -} // namespace internal +template<typename R, typename P1, typename P2, typename P3, typename P4, + typename P5> +struct Dispatcher<R(P1, P2, P3, P4, P5)> { + static void DispatchToCallback( + const v8::FunctionCallbackInfo<v8::Value>& info) { + Arguments args(info); + v8::Handle<v8::External> v8_holder; + CHECK(args.GetData(&v8_holder)); + CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>( + v8_holder->Value()); + typedef CallbackHolder<R(P1, P2, P3, P4, P5)> HolderT; + HolderT* holder = static_cast<HolderT*>(holder_base); -// This should be called once per-isolate to initialize the function template -// system. -GIN_EXPORT void InitFunctionTemplates(PerIsolateData* isolate_data); + typename CallbackParamTraits<P1>::LocalType a1; + typename CallbackParamTraits<P2>::LocalType a2; + typename CallbackParamTraits<P3>::LocalType a3; + typename CallbackParamTraits<P4>::LocalType a4; + typename CallbackParamTraits<P5>::LocalType a5; + 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) || + !GetNextArgument(&args, holder->flags, false, &a5)) { + args.ThrowError(); + return; + } + + Invoker<R, P1, P2, P3, P4, P5>::Go(&args, holder->callback, a1, a2, a3, a4, + a5); + } +}; + +template<typename R, typename P1, typename P2, typename P3, typename P4, + typename P5, typename P6> +struct Dispatcher<R(P1, P2, P3, P4, P5, P6)> { + static void DispatchToCallback( + const v8::FunctionCallbackInfo<v8::Value>& info) { + Arguments args(info); + v8::Handle<v8::External> v8_holder; + CHECK(args.GetData(&v8_holder)); + CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>( + v8_holder->Value()); + + typedef CallbackHolder<R(P1, P2, P3, P4, P5, P6)> 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; + typename CallbackParamTraits<P5>::LocalType a5; + typename CallbackParamTraits<P6>::LocalType a6; + 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) || + !GetNextArgument(&args, holder->flags, false, &a5) || + !GetNextArgument(&args, holder->flags, false, &a6)) { + args.ThrowError(); + return; + } + + Invoker<R, P1, P2, P3, P4, P5, P6>::Go(&args, holder->callback, a1, a2, a3, + a4, a5, a6); + } +}; + +} // namespace internal // CreateFunctionTemplate creates a v8::FunctionTemplate that will create @@ -345,11 +492,27 @@ v8::Local<v8::FunctionTemplate> CreateFunctionTemplate( 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)); + HolderT* holder = new HolderT(isolate, callback, callback_flags); + return v8::FunctionTemplate::New( + isolate, &internal::Dispatcher<Sig>::DispatchToCallback, - ConvertToV8<internal::CallbackHolderBase*>(isolate, holder.get())); + ConvertToV8<v8::Handle<v8::External> >(isolate, + holder->GetHandle(isolate))); +} + +// CreateFunctionHandler installs a CallAsFunction handler on the given +// object template that forwards to a provided C++ function or base::Callback. +template<typename Sig> +void CreateFunctionHandler(v8::Isolate* isolate, + v8::Local<v8::ObjectTemplate> tmpl, + const base::Callback<Sig> callback, + int callback_flags = 0) { + typedef internal::CallbackHolder<Sig> HolderT; + HolderT* holder = new HolderT(isolate, callback, callback_flags); + tmpl->SetCallAsFunctionHandler(&internal::Dispatcher<Sig>::DispatchToCallback, + ConvertToV8<v8::Handle<v8::External> >( + isolate, holder->GetHandle(isolate))); } } // namespace gin diff --git a/chromium/gin/function_template.h.pump b/chromium/gin/function_template.h.pump index fad718febc2..20b2821e0ef 100644 --- a/chromium/gin/function_template.h.pump +++ b/chromium/gin/function_template.h.pump @@ -8,7 +8,7 @@ $$ #ifndef GIN_FUNCTION_TEMPLATE_H_ #define GIN_FUNCTION_TEMPLATE_H_ -$var MAX_ARITY = 4 +$var MAX_ARITY = 6 // Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be @@ -19,11 +19,6 @@ $var MAX_ARITY = 4 #include "gin/arguments.h" #include "gin/converter.h" #include "gin/gin_export.h" -#include "gin/handle.h" -#include "gin/public/gin_embedders.h" -#include "gin/public/wrapper_info.h" -#include "gin/wrappable.h" - #include "v8/include/v8.h" namespace gin { @@ -51,34 +46,41 @@ struct CallbackParamTraits<const T*> { // CallbackHolder and CallbackHolderBase are used to pass a base::Callback from -// CreateFunctionTemplate through v8 (via v8::FunctionTemplate) to +// CreateFunctionTemplate through v8 (via v8::FunctionTemplate) to // DispatchToCallback, where it is invoked. -// -// v8::FunctionTemplate only supports passing void* as data so how do we know -// when to delete the base::Callback? That's where CallbackHolderBase comes in. -// 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. // This simple base class is used so that we can share a single object template // among every CallbackHolder instance. -class GIN_EXPORT CallbackHolderBase : public Wrappable<CallbackHolderBase> { +class GIN_EXPORT CallbackHolderBase { public: - static WrapperInfo kWrapperInfo; + v8::Handle<v8::External> GetHandle(v8::Isolate* isolate); protected: - virtual ~CallbackHolderBase() {} + explicit CallbackHolderBase(v8::Isolate* isolate); + virtual ~CallbackHolderBase(); + + private: + static void WeakCallback( + const v8::WeakCallbackData<v8::External, CallbackHolderBase>& data); + + v8::Persistent<v8::External> v8_ref_; + + DISALLOW_COPY_AND_ASSIGN(CallbackHolderBase); }; template<typename Sig> class CallbackHolder : public CallbackHolderBase { public: - CallbackHolder(const base::Callback<Sig>& callback, int flags) - : callback(callback), flags(flags) {} + CallbackHolder(v8::Isolate* isolate, + const base::Callback<Sig>& callback, + int flags) + : CallbackHolderBase(isolate), callback(callback), flags(flags) {} base::Callback<Sig> callback; int flags; private: virtual ~CallbackHolder() {} + + DISALLOW_COPY_AND_ASSIGN(CallbackHolder); }; @@ -141,6 +143,18 @@ inline bool GetNextArgument(Arguments* args, int create_flags, bool is_first, *result = *args; return true; } +inline bool GetNextArgument(Arguments* args, int create_flags, bool is_first, + Arguments** result) { + *result = args; + return true; +} + +// It's common for clients to just need the isolate, so we make that easy. +inline bool GetNextArgument(Arguments* args, int create_flags, + bool is_first, v8::Isolate** result) { + *result = args->isolate(); + return true; +} // DispatchToCallback converts all the JavaScript arguments to C++ types and @@ -158,8 +172,10 @@ 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)); + v8::Handle<v8::External> v8_holder; + CHECK(args.GetData(&v8_holder)); + CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>( + v8_holder->Value()); typedef CallbackHolder<R($for ARG , [[P$(ARG)]])> HolderT; HolderT* holder = static_cast<HolderT*>(holder_base); @@ -187,11 +203,6 @@ $for ARG [[ typename CallbackParamTraits<P$(ARG)>::LocalType a$(ARG); } // namespace internal -// This should be called once per-isolate to initialize the function template -// system. -GIN_EXPORT void InitFunctionTemplates(PerIsolateData* isolate_data); - - // 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 @@ -201,11 +212,27 @@ v8::Local<v8::FunctionTemplate> CreateFunctionTemplate( 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)); + HolderT* holder = new HolderT(isolate, callback, callback_flags); + return v8::FunctionTemplate::New( + isolate, &internal::Dispatcher<Sig>::DispatchToCallback, - ConvertToV8<internal::CallbackHolderBase*>(isolate, holder.get())); + ConvertToV8<v8::Handle<v8::External> >(isolate, + holder->GetHandle(isolate))); +} + +// CreateFunctionHandler installs a CallAsFunction handler on the given +// object template that forwards to a provided C++ function or base::Callback. +template<typename Sig> +void CreateFunctionHandler(v8::Isolate* isolate, + v8::Local<v8::ObjectTemplate> tmpl, + const base::Callback<Sig> callback, + int callback_flags = 0) { + typedef internal::CallbackHolder<Sig> HolderT; + HolderT* holder = new HolderT(isolate, callback, callback_flags); + tmpl->SetCallAsFunctionHandler(&internal::Dispatcher<Sig>::DispatchToCallback, + ConvertToV8<v8::Handle<v8::External> >( + isolate, holder->GetHandle(isolate))); } } // namespace gin diff --git a/chromium/gin/gin.gyp b/chromium/gin/gin.gyp index 8af1e8b85e0..6ee606525f6 100644 --- a/chromium/gin/gin.gyp +++ b/chromium/gin/gin.gyp @@ -12,7 +12,9 @@ 'type': '<(component)', 'dependencies': [ '../base/base.gyp:base', + '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', '../v8/tools/gyp/v8.gyp:v8', + ], 'export_dependent_settings': [ '../base/base.gyp:base', @@ -35,6 +37,8 @@ 'function_template.h', 'gin_export.h', 'handle.h', + 'interceptor.cc', + 'interceptor.h', 'isolate_holder.cc', 'modules/console.cc', 'modules/console.h', @@ -42,8 +46,11 @@ 'modules/file_module_provider.h', 'modules/module_registry.cc', 'modules/module_registry.h', + 'modules/module_registry_observer.h', 'modules/module_runner_delegate.cc', 'modules/module_runner_delegate.h', + 'modules/timer.cc', + 'modules/timer.h', 'object_template_builder.cc', 'object_template_builder.h', 'per_context_data.cc', @@ -53,11 +60,15 @@ 'public/context_holder.h', 'public/gin_embedders.h', 'public/isolate_holder.h', + 'public/v8_platform.h', 'public/wrapper_info.h', 'runner.cc', 'runner.h', + 'shell_runner.cc', + 'shell_runner.h', 'try_catch.cc', 'try_catch.h', + 'v8_platform.cc', 'wrappable.cc', 'wrappable.h', 'wrapper_info.cc', @@ -96,6 +107,8 @@ 'sources': [ 'test/file_runner.cc', 'test/file_runner.h', + 'test/gc.cc', + 'test/gc.h', 'test/gtest.cc', 'test/gtest.h', 'test/v8_test.cc', @@ -106,15 +119,19 @@ 'target_name': 'gin_unittests', 'type': 'executable', 'dependencies': [ - '../base/base.gyp:run_all_unittests', + '../base/base.gyp:test_support_base', '../v8/tools/gyp/v8.gyp:v8', 'gin_test', ], 'sources': [ 'converter_unittest.cc', + 'interceptor_unittest.cc', + 'modules/module_registry_unittest.cc', + 'modules/timer_unittest.cc', + 'per_context_data_unittest.cc', + 'shell_runner_unittest.cc', 'test/run_all_unittests.cc', 'test/run_js_tests.cc', - 'runner_unittest.cc', 'wrappable_unittest.cc', ], }, diff --git a/chromium/gin/handle.h b/chromium/gin/handle.h index da1de347f23..01db6606f19 100644 --- a/chromium/gin/handle.h +++ b/chromium/gin/handle.h @@ -60,7 +60,10 @@ struct Converter<gin::Handle<T> > { // without having to write out the type of the object explicitly. template<typename T> gin::Handle<T> CreateHandle(v8::Isolate* isolate, T* object) { - return gin::Handle<T>(object->GetWrapper(isolate), object); + v8::Handle<v8::Object> wrapper = object->GetWrapper(isolate); + if (wrapper.IsEmpty()) + return gin::Handle<T>(); + return gin::Handle<T>(wrapper, object); } } // namespace gin diff --git a/chromium/gin/interceptor.cc b/chromium/gin/interceptor.cc new file mode 100644 index 00000000000..7efc32ee68b --- /dev/null +++ b/chromium/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/chromium/gin/interceptor.h b/chromium/gin/interceptor.h new file mode 100644 index 00000000000..802929b8377 --- /dev/null +++ b/chromium/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/chromium/gin/interceptor_unittest.cc b/chromium/gin/interceptor_unittest.cc new file mode 100644 index 00000000000..ee6b7dc50a7 --- /dev/null +++ b/chromium/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/chromium/gin/isolate_holder.cc b/chromium/gin/isolate_holder.cc index 1929ebd0342..d0ebbb84bf2 100644 --- a/chromium/gin/isolate_holder.cc +++ b/chromium/gin/isolate_holder.cc @@ -13,6 +13,7 @@ #include "gin/array_buffer.h" #include "gin/function_template.h" #include "gin/per_isolate_data.h" +#include "gin/public/v8_platform.h" namespace gin { @@ -23,8 +24,8 @@ bool GenerateEntropy(unsigned char* buffer, size_t amount) { return true; } - -void EnsureV8Initialized(bool gin_managed) { +void EnsureV8Initialized(gin::IsolateHolder::ScriptMode mode, + bool gin_managed) { static bool v8_is_initialized = false; static bool v8_is_gin_managed = false; if (v8_is_initialized) { @@ -36,31 +37,35 @@ void EnsureV8Initialized(bool gin_managed) { if (!gin_managed) return; + v8::V8::InitializePlatform(V8Platform::Get()); v8::V8::SetArrayBufferAllocator(ArrayBufferAllocator::SharedInstance()); - static const char v8_flags[] = "--use_strict --harmony"; - v8::V8::SetFlagsFromString(v8_flags, sizeof(v8_flags) - 1); + if (mode == gin::IsolateHolder::kStrictMode) { + static const char v8_flags[] = "--use_strict"; + v8::V8::SetFlagsFromString(v8_flags, sizeof(v8_flags) - 1); + } v8::V8::SetEntropySource(&GenerateEntropy); v8::V8::Initialize(); } } // namespace -IsolateHolder::IsolateHolder() +IsolateHolder::IsolateHolder(ScriptMode mode) : isolate_owner_(true) { - EnsureV8Initialized(true); + EnsureV8Initialized(mode, true); isolate_ = v8::Isolate::New(); v8::ResourceConstraints constraints; constraints.ConfigureDefaults(base::SysInfo::AmountOfPhysicalMemory(), + base::SysInfo::AmountOfVirtualMemory(), base::SysInfo::NumberOfProcessors()); v8::SetResourceConstraints(isolate_, &constraints); - Init(); + Init(ArrayBufferAllocator::SharedInstance()); } -IsolateHolder::IsolateHolder(v8::Isolate* isolate) - : isolate_owner_(false), - isolate_(isolate) { - EnsureV8Initialized(false); - Init(); +IsolateHolder::IsolateHolder(v8::Isolate* isolate, + v8::ArrayBuffer::Allocator* allocator) + : isolate_owner_(false), isolate_(isolate) { + EnsureV8Initialized(kNonStrictMode, false); + Init(allocator); } IsolateHolder::~IsolateHolder() { @@ -69,11 +74,10 @@ IsolateHolder::~IsolateHolder() { isolate_->Dispose(); } -void IsolateHolder::Init() { +void IsolateHolder::Init(v8::ArrayBuffer::Allocator* allocator) { v8::Isolate::Scope isolate_scope(isolate_); v8::HandleScope handle_scope(isolate_); - isolate_data_.reset(new PerIsolateData(isolate_)); - InitFunctionTemplates(isolate_data_.get()); + isolate_data_.reset(new PerIsolateData(isolate_, allocator)); } } // namespace gin diff --git a/chromium/gin/modules/console.cc b/chromium/gin/modules/console.cc index 14849944bfa..d172373f01d 100644 --- a/chromium/gin/modules/console.cc +++ b/chromium/gin/modules/console.cc @@ -9,6 +9,7 @@ #include "base/strings/string_util.h" #include "gin/arguments.h" #include "gin/converter.h" +#include "gin/object_template_builder.h" #include "gin/per_isolate_data.h" #include "gin/public/wrapper_info.h" @@ -18,13 +19,12 @@ namespace gin { namespace { -void Log(const v8::FunctionCallbackInfo<v8::Value>& info) { - Arguments args(info); - +void Log(Arguments* args) { std::vector<std::string> messages; - if (!args.GetRemaining(&messages)) - return args.ThrowTypeError("Expected strings."); - + if (!args->GetRemaining(&messages)) { + args->ThrowError(); + return; + } std::cout << JoinString(messages, ' ') << std::endl; } @@ -34,16 +34,16 @@ WrapperInfo g_wrapper_info = { kEmbedderNativeGin }; const char Console::kModuleName[] = "console"; -v8::Local<ObjectTemplate> Console::GetTemplate(v8::Isolate* isolate) { +v8::Local<v8::Value> Console::GetModule(v8::Isolate* isolate) { PerIsolateData* data = PerIsolateData::From(isolate); v8::Local<ObjectTemplate> templ = data->GetObjectTemplate(&g_wrapper_info); if (templ.IsEmpty()) { - templ = ObjectTemplate::New(); - templ->Set(StringToSymbol(isolate, "log"), - v8::FunctionTemplate::New(isolate, Log)); + templ = ObjectTemplateBuilder(isolate) + .SetMethod("log", Log) + .Build(); data->SetObjectTemplate(&g_wrapper_info, templ); } - return templ; + return templ->NewInstance(); } } // namespace gin diff --git a/chromium/gin/modules/console.h b/chromium/gin/modules/console.h index 8753961dd2b..ff8061ba4a3 100644 --- a/chromium/gin/modules/console.h +++ b/chromium/gin/modules/console.h @@ -15,7 +15,7 @@ namespace gin { class GIN_EXPORT Console { public: static const char kModuleName[]; - static v8::Local<v8::ObjectTemplate> GetTemplate(v8::Isolate* isolate); + static v8::Local<v8::Value> GetModule(v8::Isolate* isolate); }; } // namespace gin diff --git a/chromium/gin/modules/file_module_provider.cc b/chromium/gin/modules/file_module_provider.cc index 990dcd000c0..660d9b1b9ac 100644 --- a/chromium/gin/modules/file_module_provider.cc +++ b/chromium/gin/modules/file_module_provider.cc @@ -37,10 +37,7 @@ void AttempToLoadModule(const base::WeakPtr<Runner>& runner, continue; Runner::Scope scope(runner.get()); - v8::Handle<v8::Script> script = v8::Script::New( - StringToV8(runner->isolate(), source), - StringToV8(runner->isolate(), id)); - runner->Run(script); + runner->Run(source, id); return; } } diff --git a/chromium/gin/modules/module_registry.cc b/chromium/gin/modules/module_registry.cc index 9dd3dbe9810..3712f1a41ac 100644 --- a/chromium/gin/modules/module_registry.cc +++ b/chromium/gin/modules/module_registry.cc @@ -10,6 +10,8 @@ #include "base/logging.h" #include "gin/arguments.h" #include "gin/converter.h" +#include "gin/modules/module_registry_observer.h" +#include "gin/per_context_data.h" #include "gin/per_isolate_data.h" #include "gin/public/wrapper_info.h" #include "gin/runner.h" @@ -47,6 +49,13 @@ PendingModule::~PendingModule() { namespace { +// Key for base::SupportsUserData::Data. +const char kModuleRegistryKey[] = "ModuleRegistry"; + +struct ModuleRegistryData : public base::SupportsUserData::Data { + scoped_ptr<ModuleRegistry> registry; +}; + void Define(const v8::FunctionCallbackInfo<Value>& info) { Arguments args(info); @@ -87,52 +96,67 @@ Local<FunctionTemplate> GetDefineTemplate(Isolate* isolate) { return templ; } -v8::Handle<String> GetHiddenValueKey(Isolate* isolate) { - return StringToSymbol(isolate, "::gin::ModuleRegistry"); -} - } // namespace ModuleRegistry::ModuleRegistry(Isolate* isolate) - : modules_(isolate, Object::New()) { + : modules_(isolate, Object::New(isolate)) { } ModuleRegistry::~ModuleRegistry() { modules_.Reset(); } +// static void ModuleRegistry::RegisterGlobals(Isolate* isolate, v8::Handle<ObjectTemplate> templ) { templ->Set(StringToSymbol(isolate, "define"), GetDefineTemplate(isolate)); } +// static +void ModuleRegistry::InstallGlobals(v8::Isolate* isolate, + v8::Handle<v8::Object> obj) { + obj->Set(StringToSymbol(isolate, "define"), + GetDefineTemplate(isolate)->GetFunction()); +} + +// static ModuleRegistry* ModuleRegistry::From(v8::Handle<Context> context) { - Isolate* isolate = context->GetIsolate(); - v8::Handle<String> key = GetHiddenValueKey(isolate); - v8::Handle<Value> value = context->Global()->GetHiddenValue(key); - v8::Handle<External> external; - if (value.IsEmpty() || !ConvertFromV8(isolate, value, &external)) { - PerContextData* data = PerContextData::From(context); - if (!data) - return NULL; - ModuleRegistry* registry = new ModuleRegistry(isolate); - context->Global()->SetHiddenValue(key, External::New(isolate, registry)); - data->AddSupplement(scoped_ptr<ContextSupplement>(registry)); - return registry; + PerContextData* data = PerContextData::From(context); + if (!data) + return NULL; + + ModuleRegistryData* registry_data = static_cast<ModuleRegistryData*>( + data->GetUserData(kModuleRegistryKey)); + if (!registry_data) { + // PerContextData takes ownership of ModuleRegistryData. + registry_data = new ModuleRegistryData; + registry_data->registry.reset(new ModuleRegistry(context->GetIsolate())); + data->SetUserData(kModuleRegistryKey, registry_data); } - return static_cast<ModuleRegistry*>(external->Value()); + return registry_data->registry.get(); } -void ModuleRegistry::AddBuiltinModule(Isolate* isolate, - const std::string& id, - v8::Handle<ObjectTemplate> templ) { +void ModuleRegistry::AddObserver(ModuleRegistryObserver* observer) { + observer_list_.AddObserver(observer); +} + +void ModuleRegistry::RemoveObserver(ModuleRegistryObserver* observer) { + observer_list_.RemoveObserver(observer); +} + +void ModuleRegistry::AddBuiltinModule(Isolate* isolate, const std::string& id, + v8::Handle<Value> module) { DCHECK(!id.empty()); - RegisterModule(isolate, id, templ->NewInstance()); + RegisterModule(isolate, id, module); } void ModuleRegistry::AddPendingModule(Isolate* isolate, scoped_ptr<PendingModule> pending) { + const std::string pending_id = pending->id; + const std::vector<std::string> pending_dependencies = pending->dependencies; AttemptToLoad(isolate, pending.Pass()); + FOR_EACH_OBSERVER(ModuleRegistryObserver, observer_list_, + OnDidAddPendingModule(pending_id, pending_dependencies)); } void ModuleRegistry::LoadModule(Isolate* isolate, @@ -169,11 +193,6 @@ void ModuleRegistry::RegisterModule(Isolate* isolate, callback.Run(module); } -void ModuleRegistry::Detach(v8::Handle<Context> context) { - context->Global()->SetHiddenValue(GetHiddenValueKey(context->GetIsolate()), - v8::Handle<Value>()); -} - bool ModuleRegistry::CheckDependencies(PendingModule* pending) { size_t num_missing_dependencies = 0; size_t len = pending->dependencies.size(); diff --git a/chromium/gin/modules/module_registry.h b/chromium/gin/modules/module_registry.h index 27606cd9a54..5775a34a3db 100644 --- a/chromium/gin/modules/module_registry.h +++ b/chromium/gin/modules/module_registry.h @@ -13,11 +13,14 @@ #include "base/callback.h" #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/observer_list.h" #include "gin/gin_export.h" -#include "gin/per_context_data.h" +#include "v8/include/v8.h" namespace gin { +class ModuleRegistryObserver; struct PendingModule; // This class implements the Asynchronous Module Definition (AMD) API. @@ -31,7 +34,7 @@ struct PendingModule; // function. The spec says we should only add that property once our // implementation complies with the specification. // -class GIN_EXPORT ModuleRegistry : public ContextSupplement { +class GIN_EXPORT ModuleRegistry { public: typedef base::Callback<void (v8::Handle<v8::Value>)> LoadModuleCallback; @@ -42,10 +45,16 @@ class GIN_EXPORT ModuleRegistry : public ContextSupplement { static void RegisterGlobals(v8::Isolate* isolate, v8::Handle<v8::ObjectTemplate> templ); + // Installs the necessary functions needed for modules. + // WARNING: this may execute script in the page. + static void InstallGlobals(v8::Isolate* isolate, v8::Handle<v8::Object> obj); + + void AddObserver(ModuleRegistryObserver* observer); + void RemoveObserver(ModuleRegistryObserver* observer); + // The caller must have already entered our context. - void AddBuiltinModule(v8::Isolate* isolate, - const std::string& id, - v8::Handle<v8::ObjectTemplate> templ); + void AddBuiltinModule(v8::Isolate* isolate, const std::string& id, + v8::Handle<v8::Value> module); // The caller must have already entered our context. void AddPendingModule(v8::Isolate* isolate, @@ -72,9 +81,6 @@ class GIN_EXPORT ModuleRegistry : public ContextSupplement { explicit ModuleRegistry(v8::Isolate* isolate); - // From ContextSupplement: - virtual void Detach(v8::Handle<v8::Context> context) OVERRIDE; - void Load(v8::Isolate* isolate, scoped_ptr<PendingModule> pending); void RegisterModule(v8::Isolate* isolate, const std::string& id, @@ -93,6 +99,8 @@ class GIN_EXPORT ModuleRegistry : public ContextSupplement { PendingModuleVector pending_modules_; v8::Persistent<v8::Object> modules_; + ObserverList<ModuleRegistryObserver> observer_list_; + DISALLOW_COPY_AND_ASSIGN(ModuleRegistry); }; diff --git a/chromium/gin/modules/module_registry_observer.h b/chromium/gin/modules/module_registry_observer.h new file mode 100644 index 00000000000..68ee4adc759 --- /dev/null +++ b/chromium/gin/modules/module_registry_observer.h @@ -0,0 +1,31 @@ +// 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_MODULES_MODULE_REGISTRY_OBSERVER_H_ +#define GIN_MODULES_MODULE_REGISTRY_OBSERVER_H_ + +#include <string> +#include <vector> + +#include "gin/gin_export.h" + +namespace gin { + +// Notified of interesting events from ModuleRegistry. +class GIN_EXPORT ModuleRegistryObserver { + public: + // Called from AddPendingModule(). |id| is the id/name of the module and + // |dependencies| this list of modules |id| depends upon. + virtual void OnDidAddPendingModule( + const std::string& id, + const std::vector<std::string>& dependencies) = 0; + + protected: + virtual ~ModuleRegistryObserver() {} +}; + +} // namespace gin + +#endif // GIN_MODULES_MODULE_REGISTRY_OBSERVER_H_ + diff --git a/chromium/gin/modules/module_registry_unittest.cc b/chromium/gin/modules/module_registry_unittest.cc new file mode 100644 index 00000000000..0ffdc0c3dd5 --- /dev/null +++ b/chromium/gin/modules/module_registry_unittest.cc @@ -0,0 +1,99 @@ +// 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/modules/module_registry.h" + +#include "base/message_loop/message_loop.h" +#include "gin/modules/module_registry_observer.h" +#include "gin/modules/module_runner_delegate.h" +#include "gin/public/context_holder.h" +#include "gin/public/isolate_holder.h" +#include "gin/shell_runner.h" +#include "gin/test/v8_test.h" +#include "v8/include/v8.h" + +namespace gin { + +namespace { + +struct TestHelper { + TestHelper(v8::Isolate* isolate) + : delegate(std::vector<base::FilePath>()), + runner(new ShellRunner(&delegate, isolate)), + scope(runner.get()) { + } + + base::MessageLoop message_loop; + ModuleRunnerDelegate delegate; + scoped_ptr<ShellRunner> runner; + Runner::Scope scope; +}; + +class ModuleRegistryObserverImpl : public ModuleRegistryObserver { + public: + ModuleRegistryObserverImpl() : did_add_count_(0) {} + + virtual void OnDidAddPendingModule( + const std::string& id, + const std::vector<std::string>& dependencies) OVERRIDE { + did_add_count_++; + id_ = id; + dependencies_ = dependencies; + } + + int did_add_count() { return did_add_count_; } + const std::string& id() const { return id_; } + const std::vector<std::string>& dependencies() const { return dependencies_; } + + private: + int did_add_count_; + std::string id_; + std::vector<std::string> dependencies_; + + DISALLOW_COPY_AND_ASSIGN(ModuleRegistryObserverImpl); +}; + +} // namespace + +typedef V8Test ModuleRegistryTest; + +// Verifies ModuleRegistry is not available after ContextHolder has been +// deleted. +TEST_F(ModuleRegistryTest, DestroyedWithContext) { + v8::Isolate::Scope isolate_scope(instance_->isolate()); + v8::HandleScope handle_scope(instance_->isolate()); + v8::Handle<v8::Context> context = v8::Context::New( + instance_->isolate(), NULL, v8::Handle<v8::ObjectTemplate>()); + { + ContextHolder context_holder(instance_->isolate()); + context_holder.SetContext(context); + ModuleRegistry* registry = ModuleRegistry::From(context); + EXPECT_TRUE(registry != NULL); + } + ModuleRegistry* registry = ModuleRegistry::From(context); + EXPECT_TRUE(registry == NULL); +} + +// Verifies ModuleRegistryObserver is notified appropriately. +TEST_F(ModuleRegistryTest, ModuleRegistryObserverTest) { + TestHelper helper(instance_->isolate()); + std::string source = + "define('id', ['dep1', 'dep2'], function() {" + " return function() {};" + "});"; + + ModuleRegistryObserverImpl observer; + ModuleRegistry::From(helper.runner->GetContextHolder()->context())-> + AddObserver(&observer); + helper.runner->Run(source, "script"); + ModuleRegistry::From(helper.runner->GetContextHolder()->context())-> + RemoveObserver(&observer); + EXPECT_EQ(1, observer.did_add_count()); + EXPECT_EQ("id", observer.id()); + ASSERT_EQ(2u, observer.dependencies().size()); + EXPECT_EQ("dep1", observer.dependencies()[0]); + EXPECT_EQ("dep2", observer.dependencies()[1]); +} + +} // namespace gin diff --git a/chromium/gin/modules/module_runner_delegate.cc b/chromium/gin/modules/module_runner_delegate.cc index 9bf2863c203..16b5afd0303 100644 --- a/chromium/gin/modules/module_runner_delegate.cc +++ b/chromium/gin/modules/module_runner_delegate.cc @@ -5,6 +5,8 @@ #include "gin/modules/module_runner_delegate.h" #include "gin/modules/module_registry.h" +#include "gin/object_template_builder.h" +#include "gin/public/context_holder.h" namespace gin { @@ -17,38 +19,40 @@ ModuleRunnerDelegate::~ModuleRunnerDelegate() { } void ModuleRunnerDelegate::AddBuiltinModule(const std::string& id, - ModuleTemplateGetter templ) { - builtin_modules_[id] = templ; + ModuleGetter getter) { + builtin_modules_[id] = getter; } void ModuleRunnerDelegate::AttemptToLoadMoreModules(Runner* runner) { - ModuleRegistry* registry = ModuleRegistry::From(runner->context()); - registry->AttemptToLoadMoreModules(runner->isolate()); + ModuleRegistry* registry = ModuleRegistry::From( + runner->GetContextHolder()->context()); + registry->AttemptToLoadMoreModules(runner->GetContextHolder()->isolate()); module_provider_.AttempToLoadModules( runner, registry->unsatisfied_dependencies()); } v8::Handle<v8::ObjectTemplate> ModuleRunnerDelegate::GetGlobalTemplate( - Runner* runner) { - v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); - ModuleRegistry::RegisterGlobals(runner->isolate(), templ); + ShellRunner* runner, + v8::Isolate* isolate) { + v8::Handle<v8::ObjectTemplate> templ = ObjectTemplateBuilder(isolate).Build(); + ModuleRegistry::RegisterGlobals(isolate, templ); return templ; } -void ModuleRunnerDelegate::DidCreateContext(Runner* runner) { - RunnerDelegate::DidCreateContext(runner); +void ModuleRunnerDelegate::DidCreateContext(ShellRunner* runner) { + ShellRunnerDelegate::DidCreateContext(runner); - v8::Handle<v8::Context> context = runner->context(); + v8::Handle<v8::Context> context = runner->GetContextHolder()->context(); ModuleRegistry* registry = ModuleRegistry::From(context); + v8::Isolate* isolate = runner->GetContextHolder()->isolate(); for (BuiltinModuleMap::const_iterator it = builtin_modules_.begin(); it != builtin_modules_.end(); ++it) { - registry->AddBuiltinModule(runner->isolate(), it->first, - it->second(runner->isolate())); + registry->AddBuiltinModule(isolate, it->first, it->second(isolate)); } } -void ModuleRunnerDelegate::DidRunScript(Runner* runner) { +void ModuleRunnerDelegate::DidRunScript(ShellRunner* runner) { AttemptToLoadMoreModules(runner); } diff --git a/chromium/gin/modules/module_runner_delegate.h b/chromium/gin/modules/module_runner_delegate.h index 06077f479bb..09d4582dd76 100644 --- a/chromium/gin/modules/module_runner_delegate.h +++ b/chromium/gin/modules/module_runner_delegate.h @@ -10,40 +10,35 @@ #include "base/compiler_specific.h" #include "gin/gin_export.h" #include "gin/modules/file_module_provider.h" -#include "gin/runner.h" +#include "gin/shell_runner.h" namespace gin { -typedef v8::Local<v8::ObjectTemplate> (*ModuleTemplateGetter)( - v8::Isolate* isolate); +typedef v8::Local<v8::Value> (*ModuleGetter)(v8::Isolate* isolate); // Emebedders that use AMD modules will probably want to use a RunnerDelegate // that inherits from ModuleRunnerDelegate. ModuleRunnerDelegate lets embedders // register built-in modules and routes module requests to FileModuleProvider. -class GIN_EXPORT ModuleRunnerDelegate : public RunnerDelegate { +class GIN_EXPORT ModuleRunnerDelegate : public ShellRunnerDelegate { public: explicit ModuleRunnerDelegate( const std::vector<base::FilePath>& search_paths); virtual ~ModuleRunnerDelegate(); - // Lets you register a built-in module. Built-in modules are instantiated by - // creating a new instance of a v8::ObjectTemplate rather than by executing - // code. This function takes a ModuleTemplateGetter rather than a - // v8::ObjectTemplate directly so that embedders can create object templates - // lazily. - void AddBuiltinModule(const std::string& id, ModuleTemplateGetter templ); + void AddBuiltinModule(const std::string& id, ModuleGetter getter); protected: void AttemptToLoadMoreModules(Runner* runner); private: - typedef std::map<std::string, ModuleTemplateGetter> BuiltinModuleMap; + typedef std::map<std::string, ModuleGetter> BuiltinModuleMap; - // From RunnerDelegate: + // From ShellRunnerDelegate: virtual v8::Handle<v8::ObjectTemplate> GetGlobalTemplate( - Runner* runner) OVERRIDE; - virtual void DidCreateContext(Runner* runner) OVERRIDE; - virtual void DidRunScript(Runner* runner) OVERRIDE; + ShellRunner* runner, + v8::Isolate* isolate) OVERRIDE; + virtual void DidCreateContext(ShellRunner* runner) OVERRIDE; + virtual void DidRunScript(ShellRunner* runner) OVERRIDE; BuiltinModuleMap builtin_modules_; FileModuleProvider module_provider_; diff --git a/chromium/gin/modules/timer.cc b/chromium/gin/modules/timer.cc new file mode 100644 index 00000000000..3196dda4258 --- /dev/null +++ b/chromium/gin/modules/timer.cc @@ -0,0 +1,103 @@ +// 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/modules/timer.h" + +#include "base/bind.h" +#include "gin/object_template_builder.h" +#include "gin/per_context_data.h" + +namespace gin { + +namespace { + +v8::Handle<v8::String> GetHiddenPropertyName(v8::Isolate* isolate) { + return gin::StringToSymbol(isolate, "::gin::Timer"); +} + +} // namespace + +// Timer ======================================================================= + +gin::WrapperInfo Timer::kWrapperInfo = { gin::kEmbedderNativeGin }; + +// static +Handle<Timer> Timer::Create(TimerType type, v8::Isolate* isolate, int delay_ms, + v8::Handle<v8::Function> function) { + return CreateHandle(isolate, new Timer(isolate, type == TYPE_REPEATING, + delay_ms, function)); +} + +ObjectTemplateBuilder Timer::GetObjectTemplateBuilder(v8::Isolate* isolate) { + // We use Unretained() here because we directly own timer_, so we know it will + // be alive when these methods are called. + return Wrappable<Timer>::GetObjectTemplateBuilder(isolate) + .SetMethod("cancel", + base::Bind(&base::Timer::Stop, base::Unretained(&timer_))) + .SetMethod("reset", + base::Bind(&base::Timer::Reset, base::Unretained(&timer_))); +} + +Timer::Timer(v8::Isolate* isolate, bool repeating, int delay_ms, + v8::Handle<v8::Function> function) + : weak_factory_(this), + timer_(false, repeating), + runner_(PerContextData::From( + isolate->GetCurrentContext())->runner()->GetWeakPtr()) { + GetWrapper(runner_->GetContextHolder()->isolate())->SetHiddenValue( + GetHiddenPropertyName(isolate), function); + timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(delay_ms), + base::Bind(&Timer::OnTimerFired, weak_factory_.GetWeakPtr())); +} + +Timer::~Timer() { +} + +void Timer::OnTimerFired() { + // This can happen in spite of the weak callback because it is possible for + // a gin::Handle<> to keep this object alive past when the isolate it is part + // of is destroyed. + if (!runner_.get()) { + return; + } + + Runner::Scope scope(runner_.get()); + v8::Isolate* isolate = runner_->GetContextHolder()->isolate(); + v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast( + GetWrapper(isolate)->GetHiddenValue(GetHiddenPropertyName(isolate))); + runner_->Call(function, v8::Undefined(isolate), 0, NULL); +} + + +// TimerModule ================================================================= + +const char TimerModule::kName[] = "timer"; +WrapperInfo TimerModule::kWrapperInfo = { kEmbedderNativeGin }; + +// static +Handle<TimerModule> TimerModule::Create(v8::Isolate* isolate) { + return CreateHandle(isolate, new TimerModule()); +} + +// static +v8::Local<v8::Value> TimerModule::GetModule(v8::Isolate* isolate) { + return Create(isolate)->GetWrapper(isolate); +} + +TimerModule::TimerModule() { +} + +TimerModule::~TimerModule() { +} + +ObjectTemplateBuilder TimerModule::GetObjectTemplateBuilder( + v8::Isolate* isolate) { + return Wrappable<TimerModule>::GetObjectTemplateBuilder(isolate) + .SetMethod("createOneShot", + base::Bind(&Timer::Create, Timer::TYPE_ONE_SHOT)) + .SetMethod("createRepeating", + base::Bind(&Timer::Create, Timer::TYPE_REPEATING)); +} + +} // namespace gin diff --git a/chromium/gin/modules/timer.h b/chromium/gin/modules/timer.h new file mode 100644 index 00000000000..e2bed2e56fb --- /dev/null +++ b/chromium/gin/modules/timer.h @@ -0,0 +1,64 @@ +// 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_MODULES_TIMER_H_ +#define GIN_MODULES_TIMER_H_ + +#include "base/memory/weak_ptr.h" +#include "base/timer/timer.h" +#include "gin/gin_export.h" +#include "gin/handle.h" +#include "gin/runner.h" +#include "gin/wrappable.h" +#include "v8/include/v8.h" + +namespace gin { + +class ObjectTemplateBuilder; + +// A simple scriptable timer that can work in one-shot or repeating mode. +class GIN_EXPORT Timer : public Wrappable<Timer> { + public: + enum TimerType { + TYPE_ONE_SHOT, + TYPE_REPEATING + }; + + static WrapperInfo kWrapperInfo; + static Handle<Timer> Create(TimerType type, v8::Isolate* isolate, + int delay_ms, v8::Handle<v8::Function> function); + + virtual ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) OVERRIDE; + + private: + Timer(v8::Isolate* isolate, bool repeating, int delay_ms, + v8::Handle<v8::Function> function); + virtual ~Timer(); + void OnTimerFired(); + + base::WeakPtrFactory<Timer> weak_factory_; + base::Timer timer_; + base::WeakPtr<gin::Runner> runner_; +}; + + +class GIN_EXPORT TimerModule : public Wrappable<TimerModule> { + public: + static const char kName[]; + static WrapperInfo kWrapperInfo; + static Handle<TimerModule> Create(v8::Isolate* isolate); + static v8::Local<v8::Value> GetModule(v8::Isolate* isolate); + + private: + TimerModule(); + virtual ~TimerModule(); + + virtual ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) OVERRIDE; +}; + +} // namespace gin + +#endif // GIN_MODULES_TIMER_H_ diff --git a/chromium/gin/modules/timer_unittest.cc b/chromium/gin/modules/timer_unittest.cc new file mode 100644 index 00000000000..f7fd8f22e67 --- /dev/null +++ b/chromium/gin/modules/timer_unittest.cc @@ -0,0 +1,155 @@ +// 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/modules/timer.h" + +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "gin/handle.h" +#include "gin/object_template_builder.h" +#include "gin/public/isolate_holder.h" +#include "gin/shell_runner.h" +#include "gin/test/v8_test.h" +#include "gin/try_catch.h" +#include "gin/wrappable.h" +#include "v8/include/v8.h" + +namespace gin { + +namespace { + +class Result : public Wrappable<Result> { + public: + static WrapperInfo kWrapperInfo; + static Handle<Result> Create(v8::Isolate* isolate) { + return CreateHandle(isolate, new Result()); + } + + int count() const { return count_; } + void set_count(int count) { count_ = count; } + + void Quit() { + base::MessageLoop::current()->QuitNow(); + } + + private: + Result() : count_(0) { + } + + virtual ~Result() { + } + + virtual ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) OVERRIDE { + return Wrappable<Result>::GetObjectTemplateBuilder(isolate) + .SetProperty("count", &Result::count, &Result::set_count) + .SetMethod("quit", &Result::Quit); + } + + int count_; +}; + +WrapperInfo Result::kWrapperInfo = { gin::kEmbedderNativeGin }; + +struct TestHelper { + TestHelper(v8::Isolate* isolate) + : runner(new ShellRunner(&delegate, isolate)), + scope(runner.get()), + timer_module(TimerModule::Create(isolate)), + result(Result::Create(isolate)) { + EXPECT_FALSE(runner->global().IsEmpty()); + runner->global()->Set(StringToV8(isolate, "timer"), + timer_module->GetWrapper(isolate)); + runner->global()->Set(StringToV8(isolate, "result"), + result->GetWrapper(isolate)); + } + + void QuitSoon() { + loop.PostDelayedTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), + base::TimeDelta::FromMilliseconds(0)); + } + + ShellRunnerDelegate delegate; + scoped_ptr<ShellRunner> runner; + Runner::Scope scope; + Handle<TimerModule> timer_module; + Handle<Result> result; + base::MessageLoop loop; +}; + +} // namespace + +typedef V8Test TimerUnittest; + +TEST_F(TimerUnittest, OneShot) { + TestHelper helper(instance_->isolate()); + std::string source = + "timer.createOneShot(0, function() {" + " result.count++;" + "});"; + + helper.runner->Run(source, "script"); + EXPECT_EQ(0, helper.result->count()); + + helper.QuitSoon(); + helper.loop.Run(); + EXPECT_EQ(1, helper.result->count()); +} + +TEST_F(TimerUnittest, OneShotCancel) { + TestHelper helper(instance_->isolate()); + std::string source = + "var t = timer.createOneShot(0, function() {" + " result.count++;" + "});" + "t.cancel()"; + + helper.runner->Run(source, "script"); + EXPECT_EQ(0, helper.result->count()); + + helper.QuitSoon(); + helper.loop.Run(); + EXPECT_EQ(0, helper.result->count()); +} + +TEST_F(TimerUnittest, Repeating) { + TestHelper helper(instance_->isolate()); + + // TODO(aa): Cannot do: if (++result.count == 3) because of v8 bug. Create + // test case and report. + std::string source = + "timer.createRepeating(0, function() {" + " result.count++;" + " if (result.count == 3) {" + " result.quit();" + " }" + "});"; + + helper.runner->Run(source, "script"); + EXPECT_EQ(0, helper.result->count()); + + helper.loop.Run(); + EXPECT_EQ(3, helper.result->count()); +} + +TEST_F(TimerUnittest, TimerCallbackToDestroyedRunner) { + TestHelper helper(instance_->isolate()); + std::string source = + "timer.createOneShot(0, function() {" + " result.count++;" + "});"; + + helper.runner->Run(source, "script"); + EXPECT_EQ(0, helper.result->count()); + + // Destroy runner, which should destroy the timer object we created. + helper.QuitSoon(); + helper.runner.reset(NULL); + helper.loop.Run(); + + // Timer should not have run because it was deleted. + EXPECT_EQ(0, helper.result->count()); +} + +} // namespace gin diff --git a/chromium/gin/object_template_builder.cc b/chromium/gin/object_template_builder.cc index 6dc6d2fe849..603166cfabf 100644 --- a/chromium/gin/object_template_builder.cc +++ b/chromium/gin/object_template_builder.cc @@ -4,15 +4,158 @@ #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); } 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/chromium/gin/object_template_builder.h b/chromium/gin/object_template_builder.h index 6367b71245c..3d025a9deb1 100644 --- a/chromium/gin/object_template_builder.h +++ b/chromium/gin/object_template_builder.h @@ -27,6 +27,11 @@ struct CallbackTraits { T callback) { return CreateFunctionTemplate(isolate, base::Bind(callback)); } + static void SetAsFunctionHandler(v8::Isolate* isolate, + v8::Local<v8::ObjectTemplate> tmpl, + T callback) { + CreateFunctionHandler(isolate, tmpl, base::Bind(callback)); + } }; // Specialization for base::Callback. @@ -36,6 +41,11 @@ struct CallbackTraits<base::Callback<T> > { v8::Isolate* isolate, const base::Callback<T>& callback) { return CreateFunctionTemplate(isolate, callback); } + static void SetAsFunctionHandler(v8::Isolate* isolate, + v8::Local<v8::ObjectTemplate> tmpl, + const base::Callback<T>& callback) { + CreateFunctionHandler(isolate, tmpl, callback); + } }; // Specialization for member function pointers. We need to handle this case @@ -50,6 +60,12 @@ struct CallbackTraits<T, typename base::enable_if< return CreateFunctionTemplate(isolate, base::Bind(callback), HolderIsFirstArgument); } + static void SetAsFunctionHandler(v8::Isolate* isolate, + v8::Local<v8::ObjectTemplate> tmpl, + T callback) { + CreateFunctionHandler( + isolate, tmpl, base::Bind(callback), HolderIsFirstArgument); + } }; // This specialization allows people to construct function templates directly if @@ -103,6 +119,13 @@ class GIN_EXPORT ObjectTemplateBuilder { CallbackTraits<T>::CreateTemplate(isolate_, getter), CallbackTraits<U>::CreateTemplate(isolate_, setter)); } + template<typename T> + ObjectTemplateBuilder& SetCallAsFunctionHandler(const T& callback) { + CallbackTraits<T>::SetAsFunctionHandler(isolate_, template_, callback); + return *this; + } + ObjectTemplateBuilder& AddNamedPropertyInterceptor(); + ObjectTemplateBuilder& AddIndexedPropertyInterceptor(); v8::Local<v8::ObjectTemplate> Build(); diff --git a/chromium/gin/per_context_data.cc b/chromium/gin/per_context_data.cc index 5183d00102b..178c0d122c1 100644 --- a/chromium/gin/per_context_data.cc +++ b/chromium/gin/per_context_data.cc @@ -10,43 +10,24 @@ namespace gin { -ContextSupplement::ContextSupplement() { -} - -ContextSupplement::~ContextSupplement() { -} - -PerContextData::PerContextData(v8::Handle<v8::Context> context) - : runner_(NULL) { +PerContextData::PerContextData(ContextHolder* context_holder, + v8::Handle<v8::Context> context) + : context_holder_(context_holder), + runner_(NULL) { context->SetAlignedPointerInEmbedderData( kPerContextDataStartIndex + kEmbedderNativeGin, this); } PerContextData::~PerContextData() { - DCHECK(supplements_.empty()); -} - -void PerContextData::Detach(v8::Handle<v8::Context> context) { - DCHECK(From(context) == this); - context->SetAlignedPointerInEmbedderData( + v8::HandleScope handle_scope(context_holder_->isolate()); + context_holder_->context()->SetAlignedPointerInEmbedderData( kPerContextDataStartIndex + kEmbedderNativeGin, NULL); - - SuplementVector supplements; - supplements.swap(supplements_); - - for (SuplementVector::iterator it = supplements.begin(); - it != supplements.end(); ++it) { - (*it)->Detach(context); - } } +// static PerContextData* PerContextData::From(v8::Handle<v8::Context> context) { return static_cast<PerContextData*>( context->GetAlignedPointerFromEmbedderData(kEncodedValueIndex)); } -void PerContextData::AddSupplement(scoped_ptr<ContextSupplement> supplement) { - supplements_.push_back(supplement.release()); -} - } // namespace gin diff --git a/chromium/gin/per_context_data.h b/chromium/gin/per_context_data.h index 3ad68d2d37c..0d1165345df 100644 --- a/chromium/gin/per_context_data.h +++ b/chromium/gin/per_context_data.h @@ -6,53 +6,39 @@ #define GIN_PER_CONTEXT_DATA_H_ #include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/scoped_vector.h" +#include "base/supports_user_data.h" #include "gin/gin_export.h" #include "v8/include/v8.h" namespace gin { +class ContextHolder; class Runner; -// Embedders can store additional per-context data by subclassing -// ContextSupplement. -class GIN_EXPORT ContextSupplement { - public: - ContextSupplement(); - virtual ~ContextSupplement(); - - // Detach will be called before ContextHolder disposes the v8::Context. - // Embedders should not interact with |context| after Detach has been called. - virtual void Detach(v8::Handle<v8::Context> context) = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(ContextSupplement); -}; - // There is one instance of PerContextData per v8::Context managed by Gin. This -// class stores all the Gin-related data that varies per context. -class GIN_EXPORT PerContextData { +// class stores all the Gin-related data that varies per context. Arbitrary data +// can be associated with this class by way of the SupportsUserData methods. +// Instances of this class (and any associated user data) are destroyed before +// the associated v8::Context. +class GIN_EXPORT PerContextData : public base::SupportsUserData { public: - explicit PerContextData(v8::Handle<v8::Context> context); - ~PerContextData(); + PerContextData(ContextHolder* context_holder, + v8::Handle<v8::Context> context); + virtual ~PerContextData(); // Can return NULL after the ContextHolder has detached from context. - static PerContextData* From(v8::Handle<v8::Context>); - void Detach(v8::Handle<v8::Context> context); + static PerContextData* From(v8::Handle<v8::Context> context); // The Runner associated with this context. To execute script in this context, // please use the appropriate API on Runner. Runner* runner() const { return runner_; } void set_runner(Runner* runner) { runner_ = runner; } - void AddSupplement(scoped_ptr<ContextSupplement> supplement); + ContextHolder* context_holder() { return context_holder_; } private: - typedef ScopedVector<ContextSupplement> SuplementVector; - + ContextHolder* context_holder_; Runner* runner_; - SuplementVector supplements_; DISALLOW_COPY_AND_ASSIGN(PerContextData); }; diff --git a/chromium/gin/per_context_data_unittest.cc b/chromium/gin/per_context_data_unittest.cc new file mode 100644 index 00000000000..4d795871950 --- /dev/null +++ b/chromium/gin/per_context_data_unittest.cc @@ -0,0 +1,34 @@ +// 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/per_context_data.h" + +#include "gin/public/context_holder.h" +#include "gin/public/isolate_holder.h" +#include "gin/test/v8_test.h" +#include "v8/include/v8.h" + +namespace gin { + +typedef V8Test PerContextDataTest; + +// Verifies PerContextData can be looked up by context and that it is not +// available once ContextHolder is destroyed. +TEST_F(PerContextDataTest, LookupAndDestruction) { + v8::Isolate::Scope isolate_scope(instance_->isolate()); + v8::HandleScope handle_scope(instance_->isolate()); + v8::Handle<v8::Context> context = v8::Context::New( + instance_->isolate(), NULL, v8::Handle<v8::ObjectTemplate>()); + { + ContextHolder context_holder(instance_->isolate()); + context_holder.SetContext(context); + PerContextData* per_context_data = PerContextData::From(context); + EXPECT_TRUE(per_context_data != NULL); + EXPECT_EQ(&context_holder, per_context_data->context_holder()); + } + PerContextData* per_context_data = PerContextData::From(context); + EXPECT_TRUE(per_context_data == NULL); +} + +} // namespace gin diff --git a/chromium/gin/per_isolate_data.cc b/chromium/gin/per_isolate_data.cc index 6c2397ba50b..99c928cdd8e 100644 --- a/chromium/gin/per_isolate_data.cc +++ b/chromium/gin/per_isolate_data.cc @@ -2,9 +2,12 @@ // 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 "base/message_loop/message_loop_proxy.h" #include "gin/per_isolate_data.h" #include "gin/public/gin_embedders.h" +using v8::ArrayBuffer; using v8::Eternal; using v8::Isolate; using v8::Local; @@ -14,8 +17,11 @@ using v8::ObjectTemplate; namespace gin { -PerIsolateData::PerIsolateData(Isolate* isolate) - : isolate_(isolate) { +PerIsolateData::PerIsolateData(Isolate* isolate, + ArrayBuffer::Allocator* allocator) + : isolate_(isolate), + allocator_(allocator), + message_loop_proxy_(base::MessageLoopProxy::current()) { isolate_->SetData(kEmbedderNativeGin, this); } @@ -53,4 +59,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/chromium/gin/per_isolate_data.h b/chromium/gin/per_isolate_data.h index ed935454749..bffe5fb2c6f 100644 --- a/chromium/gin/per_isolate_data.h +++ b/chromium/gin/per_isolate_data.h @@ -8,17 +8,26 @@ #include <map> #include "base/basictypes.h" +#include "base/memory/ref_counted.h" #include "gin/gin_export.h" #include "gin/public/wrapper_info.h" #include "v8/include/v8.h" +namespace base { +class MessageLoopProxy; +} + 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 { public: - explicit PerIsolateData(v8::Isolate* isolate); + PerIsolateData(v8::Isolate* isolate, v8::ArrayBuffer::Allocator* allocator); ~PerIsolateData(); static PerIsolateData* From(v8::Isolate* isolate); @@ -38,19 +47,47 @@ 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_; } + base::MessageLoopProxy* message_loop_proxy() { + return message_loop_proxy_.get(); + } private: typedef std::map< 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. v8::Isolate* isolate_; + v8::ArrayBuffer::Allocator* allocator_; ObjectTemplateMap object_templates_; FunctionTemplateMap function_templates_; + IndexedPropertyInterceptorMap indexed_interceptors_; + NamedPropertyInterceptorMap named_interceptors_; + scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; DISALLOW_COPY_AND_ASSIGN(PerIsolateData); }; diff --git a/chromium/gin/public/context_holder.h b/chromium/gin/public/context_holder.h index 09fe248a373..afbcf23d739 100644 --- a/chromium/gin/public/context_holder.h +++ b/chromium/gin/public/context_holder.h @@ -25,9 +25,7 @@ enum ContextEmbedderDataFields { class PerContextData; -// ContextHolder is a generic class for holding a v8::Context. Rather than -// using ContextHolder directly, most code should use a subclass of -// ContextHolder, such as Runner. +// ContextHolder is a generic class for holding a v8::Context. class GIN_EXPORT ContextHolder { public: explicit ContextHolder(v8::Isolate* isolate); @@ -43,7 +41,7 @@ class GIN_EXPORT ContextHolder { private: v8::Isolate* isolate_; - v8::Persistent<v8::Context> context_; + v8::UniquePersistent<v8::Context> context_; scoped_ptr<PerContextData> data_; DISALLOW_COPY_AND_ASSIGN(ContextHolder); diff --git a/chromium/gin/public/isolate_holder.h b/chromium/gin/public/isolate_holder.h index d68e4d5a58b..ee696f6550b 100644 --- a/chromium/gin/public/isolate_holder.h +++ b/chromium/gin/public/isolate_holder.h @@ -8,10 +8,7 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "gin/gin_export.h" - -namespace v8 { -class Isolate; -} +#include "v8/include/v8.h" namespace gin { @@ -19,25 +16,31 @@ class PerIsolateData; // To embed Gin, first create an instance of IsolateHolder to hold the // v8::Isolate in which you will execute JavaScript. You might wish to subclass -// IsolateHolder if you want to tie more state to the lifetime of the +// IsolateHolder if you want to tie more state to the lifetime of the isolate. // // You can use gin in two modes: either gin manages V8, or the gin-embedder -// manages gin. If gin manages V8, use the IsolateHolder constructor without -// parameters, otherwise, the gin-embedder needs to create v8::Isolates and -// pass them to IsolateHolder. +// manages gin. If gin manages V8, use the IsolateHolder constructor that does +// not take an v8::Isolate parameter, otherwise, the gin-embedder needs to +// create v8::Isolates and pass them to IsolateHolder. // // It is not possible to mix the two. class GIN_EXPORT IsolateHolder { public: - IsolateHolder(); - explicit IsolateHolder(v8::Isolate* isolate); + // Controls whether or not V8 should only accept strict mode scripts. + enum ScriptMode { + kNonStrictMode, + kStrictMode + }; + + explicit IsolateHolder(ScriptMode mode); + IsolateHolder(v8::Isolate* isolate, v8::ArrayBuffer::Allocator* allocator); ~IsolateHolder(); v8::Isolate* isolate() { return isolate_; } private: - void Init(); + void Init(v8::ArrayBuffer::Allocator* allocator); bool isolate_owner_; v8::Isolate* isolate_; diff --git a/chromium/gin/public/v8_platform.h b/chromium/gin/public/v8_platform.h new file mode 100644 index 00000000000..2df0f848e59 --- /dev/null +++ b/chromium/gin/public/v8_platform.h @@ -0,0 +1,38 @@ +// 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_PUBLIC_V8_PLATFORM_H_ +#define GIN_PUBLIC_V8_PLATFORM_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/lazy_instance.h" +#include "gin/gin_export.h" +#include "v8/include/v8-platform.h" + +namespace gin { + +// A v8::Platform implementation to use with gin. +class GIN_EXPORT V8Platform : public NON_EXPORTED_BASE(v8::Platform) { + public: + static V8Platform* Get(); + + // v8::Platform implementation. + virtual void CallOnBackgroundThread( + v8::Task* task, + v8::Platform::ExpectedRuntime expected_runtime) OVERRIDE; + virtual void CallOnForegroundThread(v8::Isolate* isolate, + v8::Task* task) OVERRIDE; + private: + friend struct base::DefaultLazyInstanceTraits<V8Platform>; + + V8Platform(); + virtual ~V8Platform(); + + DISALLOW_COPY_AND_ASSIGN(V8Platform); +}; + +} // namespace gin + +#endif // GIN_PUBLIC_V8_PLATFORM_H_ diff --git a/chromium/gin/runner.cc b/chromium/gin/runner.cc index 37e16a09c74..6f018b165b2 100644 --- a/chromium/gin/runner.cc +++ b/chromium/gin/runner.cc @@ -4,96 +4,18 @@ #include "gin/runner.h" -#include "gin/converter.h" -#include "gin/per_context_data.h" -#include "gin/try_catch.h" - -using v8::Context; -using v8::HandleScope; -using v8::Isolate; -using v8::Object; -using v8::ObjectTemplate; -using v8::Script; - namespace gin { -RunnerDelegate::RunnerDelegate() { -} - -RunnerDelegate::~RunnerDelegate() { -} - -v8::Handle<ObjectTemplate> RunnerDelegate::GetGlobalTemplate(Runner* runner) { - return v8::Handle<ObjectTemplate>(); -} - -void RunnerDelegate::DidCreateContext(Runner* runner) { -} - -void RunnerDelegate::WillRunScript(Runner* runner) { -} - -void RunnerDelegate::DidRunScript(Runner* runner) { -} - -void RunnerDelegate::UnhandledException(Runner* runner, TryCatch& try_catch) { -} - -Runner::Runner(RunnerDelegate* delegate, Isolate* isolate) - : ContextHolder(isolate), - delegate_(delegate), - weak_factory_(this) { - v8::Isolate::Scope isolate_scope(isolate); - HandleScope handle_scope(isolate); - v8::Handle<v8::Context> context = - Context::New(isolate, NULL, delegate_->GetGlobalTemplate(this)); - - SetContext(context); - PerContextData::From(context)->set_runner(this); - - v8::Context::Scope scope(context); - delegate_->DidCreateContext(this); +Runner::Runner() : weak_factory_(this) { } Runner::~Runner() { } -void Runner::Run(const std::string& source, const std::string& resource_name) { - Run(Script::New(StringToV8(isolate(), source), - StringToV8(isolate(), resource_name))); -} - -void Runner::Run(v8::Handle<Script> script) { - TryCatch try_catch; - delegate_->WillRunScript(this); - - script->Run(); - - delegate_->DidRunScript(this); - if (try_catch.HasCaught()) - delegate_->UnhandledException(this, try_catch); -} - -v8::Handle<v8::Value> Runner::Call(v8::Handle<v8::Function> function, - v8::Handle<v8::Value> receiver, - int argc, - v8::Handle<v8::Value> argv[]) { - TryCatch try_catch; - delegate_->WillRunScript(this); - - v8::Handle<v8::Value> result = function->Call(receiver, argc, argv); - - delegate_->DidRunScript(this); - if (try_catch.HasCaught()) - delegate_->UnhandledException(this, try_catch); - - return result; -} - Runner::Scope::Scope(Runner* runner) - : isolate_scope_(runner->isolate()), - handle_scope_(runner->isolate()), - scope_(runner->context()) { + : isolate_scope_(runner->GetContextHolder()->isolate()), + handle_scope_(runner->GetContextHolder()->isolate()), + scope_(runner->GetContextHolder()->context()) { } Runner::Scope::~Scope() { diff --git a/chromium/gin/runner.h b/chromium/gin/runner.h index 943bcedba1c..36a75d2f956 100644 --- a/chromium/gin/runner.h +++ b/chromium/gin/runner.h @@ -10,47 +10,28 @@ #include "base/memory/weak_ptr.h" #include "gin/gin_export.h" #include "gin/public/context_holder.h" +#include "v8/include/v8.h" namespace gin { -class Runner; -class TryCatch; - -// Subclass RunnerDelegate to customize the behavior of |Runner|. Typical -// embedders will want to subclass one of the specialized RunnerDelegates, -// such as ModuleRunnerDelegate. -class GIN_EXPORT RunnerDelegate { - public: - RunnerDelegate(); - virtual ~RunnerDelegate(); - - // Returns the template for the global object. - virtual v8::Handle<v8::ObjectTemplate> GetGlobalTemplate(Runner* runner); - virtual void DidCreateContext(Runner* runner); - virtual void WillRunScript(Runner* runner); - virtual void DidRunScript(Runner* runner); - virtual void UnhandledException(Runner* runner, TryCatch& try_catch); -}; - -// Runner lets you run code in a v8::Context. Upon construction, Runner will -// create a v8::Context. Upon destruction, Runner will dispose the context. -class GIN_EXPORT Runner : public ContextHolder { +// Runner is responsible for running code in a v8::Context. +class GIN_EXPORT Runner { public: - Runner(RunnerDelegate* delegate, v8::Isolate* isolate); - ~Runner(); + Runner(); + virtual ~Runner(); // Before running script in this context, you'll need to enter the runner's // context by creating an instance of Runner::Scope on the stack. - void Run(const std::string& source, const std::string& resource_name); - void Run(v8::Handle<v8::Script> script); - - v8::Handle<v8::Value> Call(v8::Handle<v8::Function> function, - v8::Handle<v8::Value> receiver, - int argc, - v8::Handle<v8::Value> argv[]); - - v8::Handle<v8::Object> global() const { - return context()->Global(); + virtual void Run(const std::string& source, + const std::string& resource_name) = 0; + virtual v8::Handle<v8::Value> Call(v8::Handle<v8::Function> function, + v8::Handle<v8::Value> receiver, + int argc, + v8::Handle<v8::Value> argv[]) = 0; + virtual ContextHolder* GetContextHolder() = 0; + + v8::Handle<v8::Object> global() { + return GetContextHolder()->context()->Global(); } // Useful for running script in this context asynchronously. Rather than @@ -75,8 +56,6 @@ class GIN_EXPORT Runner : public ContextHolder { private: friend class Scope; - RunnerDelegate* delegate_; - base::WeakPtrFactory<Runner> weak_factory_; DISALLOW_COPY_AND_ASSIGN(Runner); diff --git a/chromium/gin/shell/gin_main.cc b/chromium/gin/shell/gin_main.cc index 24fa6df1528..8fc946502d5 100644 --- a/chromium/gin/shell/gin_main.cc +++ b/chromium/gin/shell/gin_main.cc @@ -33,17 +33,17 @@ void Run(base::WeakPtr<Runner> runner, const base::FilePath& path) { std::vector<base::FilePath> GetModuleSearchPaths() { std::vector<base::FilePath> module_base(1); - CHECK(file_util::GetCurrentDirectory(&module_base[0])); + CHECK(base::GetCurrentDirectory(&module_base[0])); return module_base; } class ShellRunnerDelegate : public ModuleRunnerDelegate { public: ShellRunnerDelegate() : ModuleRunnerDelegate(GetModuleSearchPaths()) { - AddBuiltinModule(Console::kModuleName, Console::GetTemplate); + AddBuiltinModule(Console::kModuleName, Console::GetModule); } - virtual void UnhandledException(Runner* runner, + virtual void UnhandledException(ShellRunner* runner, TryCatch& try_catch) OVERRIDE { ModuleRunnerDelegate::UnhandledException(runner, try_catch); LOG(ERROR) << try_catch.GetStackTrace(); @@ -61,12 +61,12 @@ int main(int argc, char** argv) { CommandLine::Init(argc, argv); base::i18n::InitializeICU(); - gin::IsolateHolder instance; + gin::IsolateHolder instance(gin::IsolateHolder::kStrictMode); base::MessageLoop message_loop; gin::ShellRunnerDelegate delegate; - gin::Runner runner(&delegate, instance.isolate()); + gin::ShellRunner runner(&delegate, instance.isolate()); { gin::Runner::Scope scope(&runner); diff --git a/chromium/gin/shell_runner.cc b/chromium/gin/shell_runner.cc new file mode 100644 index 00000000000..8d98e425d74 --- /dev/null +++ b/chromium/gin/shell_runner.cc @@ -0,0 +1,112 @@ +// 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/shell_runner.h" + +#include "gin/converter.h" +#include "gin/modules/module_registry.h" +#include "gin/per_context_data.h" +#include "gin/public/context_holder.h" +#include "gin/try_catch.h" + +using v8::Context; +using v8::HandleScope; +using v8::Isolate; +using v8::Object; +using v8::ObjectTemplate; +using v8::Script; + +namespace gin { + +ShellRunnerDelegate::ShellRunnerDelegate() { +} + +ShellRunnerDelegate::~ShellRunnerDelegate() { +} + +v8::Handle<ObjectTemplate> ShellRunnerDelegate::GetGlobalTemplate( + ShellRunner* runner, + v8::Isolate* isolate) { + return v8::Handle<ObjectTemplate>(); +} + +void ShellRunnerDelegate::DidCreateContext(ShellRunner* runner) { +} + +void ShellRunnerDelegate::WillRunScript(ShellRunner* runner) { +} + +void ShellRunnerDelegate::DidRunScript(ShellRunner* runner) { +} + +void ShellRunnerDelegate::UnhandledException(ShellRunner* runner, + TryCatch& try_catch) { + CHECK(false) << try_catch.GetStackTrace(); +} + +ShellRunner::ShellRunner(ShellRunnerDelegate* delegate, Isolate* isolate) + : delegate_(delegate) { + v8::Isolate::Scope isolate_scope(isolate); + HandleScope handle_scope(isolate); + v8::Handle<v8::Context> context = + Context::New(isolate, NULL, delegate_->GetGlobalTemplate(this, isolate)); + + context_holder_.reset(new ContextHolder(isolate)); + context_holder_->SetContext(context); + PerContextData::From(context)->set_runner(this); + + v8::Context::Scope scope(context); + delegate_->DidCreateContext(this); +} + +ShellRunner::~ShellRunner() { +} + +void ShellRunner::Run(const std::string& source, + const std::string& resource_name) { + TryCatch try_catch; + v8::Isolate* isolate = GetContextHolder()->isolate(); + v8::Handle<Script> script = Script::Compile( + StringToV8(isolate, source), StringToV8(isolate, resource_name)); + if (try_catch.HasCaught()) { + delegate_->UnhandledException(this, try_catch); + return; + } + + Run(script); +} + +v8::Handle<v8::Value> ShellRunner::Call(v8::Handle<v8::Function> function, + v8::Handle<v8::Value> receiver, + int argc, + v8::Handle<v8::Value> argv[]) { + TryCatch try_catch; + delegate_->WillRunScript(this); + + v8::Handle<v8::Value> result = function->Call(receiver, argc, argv); + + delegate_->DidRunScript(this); + if (try_catch.HasCaught()) + delegate_->UnhandledException(this, try_catch); + + return result; +} + +ContextHolder* ShellRunner::GetContextHolder() { + return context_holder_.get(); +} + +void ShellRunner::Run(v8::Handle<Script> script) { + TryCatch try_catch; + delegate_->WillRunScript(this); + + script->Run(); + + delegate_->DidRunScript(this); + if (try_catch.HasCaught()) { + delegate_->UnhandledException(this, try_catch); + } +} + +} // namespace gin diff --git a/chromium/gin/shell_runner.h b/chromium/gin/shell_runner.h new file mode 100644 index 00000000000..645bc8e6df1 --- /dev/null +++ b/chromium/gin/shell_runner.h @@ -0,0 +1,68 @@ +// 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_SHELL_RUNNER_H_ +#define GIN_SHELL_RUNNER_H_ + +#include "gin/runner.h" + +namespace gin { + +class ContextHolder; +class ShellRunner; +class TryCatch; + +// Subclass ShellRunnerDelegate to customize the behavior of ShellRunner. +// Typical embedders will want to subclass one of the specialized +// ShellRunnerDelegates, such as ModuleRunnerDelegate. +class GIN_EXPORT ShellRunnerDelegate { + public: + ShellRunnerDelegate(); + virtual ~ShellRunnerDelegate(); + + // Returns the template for the global object. + virtual v8::Handle<v8::ObjectTemplate> GetGlobalTemplate( + ShellRunner* runner, + v8::Isolate* isolate); + virtual void DidCreateContext(ShellRunner* runner); + virtual void WillRunScript(ShellRunner* runner); + virtual void DidRunScript(ShellRunner* runner); + virtual void UnhandledException(ShellRunner* runner, TryCatch& try_catch); +}; + +// ShellRunner executes the script/functions directly in a v8::Context. +// ShellRunner owns a ContextHolder and v8::Context, both of which are destroyed +// when the ShellRunner is deleted. +class GIN_EXPORT ShellRunner : public Runner { + public: + ShellRunner(ShellRunnerDelegate* delegate, v8::Isolate* isolate); + virtual ~ShellRunner(); + + // Before running script in this context, you'll need to enter the runner's + // context by creating an instance of Runner::Scope on the stack. + + // Runner overrides: + virtual void Run(const std::string& source, + const std::string& resource_name) OVERRIDE; + virtual v8::Handle<v8::Value> Call(v8::Handle<v8::Function> function, + v8::Handle<v8::Value> receiver, + int argc, + v8::Handle<v8::Value> argv[]) OVERRIDE; + virtual ContextHolder* GetContextHolder() OVERRIDE; + + private: + friend class Scope; + + void Run(v8::Handle<v8::Script> script); + + ShellRunnerDelegate* delegate_; + + scoped_ptr<ContextHolder> context_holder_; + + DISALLOW_COPY_AND_ASSIGN(ShellRunner); +}; + +} // namespace gin + +#endif // GIN_SHELL_RUNNER_H_ diff --git a/chromium/gin/runner_unittest.cc b/chromium/gin/shell_runner_unittest.cc index 3723956df6a..95403ecf06d 100644 --- a/chromium/gin/runner_unittest.cc +++ b/chromium/gin/shell_runner_unittest.cc @@ -1,8 +1,8 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// 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/runner.h" +#include "gin/shell_runner.h" #include "base/compiler_specific.h" #include "gin/converter.h" @@ -19,11 +19,11 @@ namespace gin { TEST(RunnerTest, Run) { std::string source = "this.result = 'PASS';\n"; - gin::IsolateHolder instance; + gin::IsolateHolder instance(gin::IsolateHolder::kStrictMode); - RunnerDelegate delegate; + ShellRunnerDelegate delegate; Isolate* isolate = instance.isolate(); - Runner runner(&delegate, isolate); + ShellRunner runner(&delegate, isolate); Runner::Scope scope(&runner); runner.Run(source, "test_data.js"); diff --git a/chromium/gin/v8_platform.cc b/chromium/gin/v8_platform.cc new file mode 100644 index 00000000000..d50ff24f8bf --- /dev/null +++ b/chromium/gin/v8_platform.cc @@ -0,0 +1,42 @@ +// 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/public/v8_platform.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/threading/worker_pool.h" +#include "gin/per_isolate_data.h" + +namespace gin { + +namespace { + +base::LazyInstance<V8Platform>::Leaky g_v8_platform = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +// static +V8Platform* V8Platform::Get() { return g_v8_platform.Pointer(); } + +V8Platform::V8Platform() {} + +V8Platform::~V8Platform() {} + +void V8Platform::CallOnBackgroundThread( + v8::Task* task, + v8::Platform::ExpectedRuntime expected_runtime) { + base::WorkerPool::PostTask( + FROM_HERE, + base::Bind(&v8::Task::Run, base::Owned(task)), + expected_runtime == v8::Platform::kLongRunningTask); +} + +void V8Platform::CallOnForegroundThread(v8::Isolate* isolate, v8::Task* task) { + PerIsolateData::From(isolate)->message_loop_proxy()->PostTask( + FROM_HERE, base::Bind(&v8::Task::Run, base::Owned(task))); +} + +} // namespace gin diff --git a/chromium/gin/wrappable.cc b/chromium/gin/wrappable.cc index 4a9ef0e1935..a330fefc8e8 100644 --- a/chromium/gin/wrappable.cc +++ b/chromium/gin/wrappable.cc @@ -5,6 +5,7 @@ #include "gin/wrappable.h" #include "base/logging.h" +#include "gin/object_template_builder.h" #include "gin/per_isolate_data.h" namespace gin { @@ -16,11 +17,9 @@ WrappableBase::~WrappableBase() { wrapper_.Reset(); } -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_); +ObjectTemplateBuilder WrappableBase::GetObjectTemplateBuilder( + v8::Isolate* isolate) { + return ObjectTemplateBuilder(isolate); } void WrappableBase::WeakCallback( @@ -30,13 +29,29 @@ void WrappableBase::WeakCallback( delete wrappable; } -v8::Handle<v8::Object> WrappableBase::CreateWrapper(v8::Isolate* isolate, - WrapperInfo* info) { +v8::Handle<v8::Object> WrappableBase::GetWrapperImpl(v8::Isolate* isolate, + WrapperInfo* info) { + if (!wrapper_.IsEmpty()) { + return v8::Local<v8::Object>::New(isolate, wrapper_); + } + PerIsolateData* data = PerIsolateData::From(isolate); v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate(info); - CHECK(!templ.IsEmpty()); // Don't forget to register an object template. + if (templ.IsEmpty()) { + templ = GetObjectTemplateBuilder(isolate).Build(); + CHECK(!templ.IsEmpty()); + data->SetObjectTemplate(info, templ); + } CHECK_EQ(kNumberOfInternalFields, templ->InternalFieldCount()); v8::Handle<v8::Object> wrapper = templ->NewInstance(); + // |wrapper| may be empty in some extreme cases, e.g., when + // Object.prototype.constructor is overwritten. + if (wrapper.IsEmpty()) { + // The current wrappable object will be no longer managed by V8. Delete this + // now. + delete this; + return wrapper; + } wrapper->SetAlignedPointerInInternalField(kWrapperInfoIndex, info); wrapper->SetAlignedPointerInInternalField(kEncodedValueIndex, this); wrapper_.Reset(isolate, wrapper); diff --git a/chromium/gin/wrappable.h b/chromium/gin/wrappable.h index 755707166bb..ff52b19a06f 100644 --- a/chromium/gin/wrappable.h +++ b/chromium/gin/wrappable.h @@ -27,11 +27,23 @@ GIN_EXPORT void* FromV8Impl(v8::Isolate* isolate, // USAGE: // // my_class.h // class MyClass : Wrappable<MyClass> { +// public: +// static WrapperInfo kWrapperInfo; +// +// // Optional, only required if non-empty template should be used. +// virtual gin::ObjectTemplateBuilder GetObjectTemplateBuilder( +// v8::Isolate* isolate); // ... // }; // // // my_class.cc -// INIT_WRAPABLE(MyClass); +// WrapperInfo MyClass::kWrapperInfo = {kEmbedderNativeGin}; +// +// gin::ObjectTemplateBuilder MyClass::GetObjectTemplateBuilder( +// v8::Isolate* isolate) { +// return Wrappable<MyClass>::GetObjectTemplateBuilder(isolate) +// .SetValue("foobar", 42); +// } // // Subclasses should also typically have private constructors and expose a // static Create function that returns a gin::Handle. Forcing creators through @@ -42,22 +54,25 @@ GIN_EXPORT void* FromV8Impl(v8::Isolate* isolate, template<typename T> class Wrappable; +class ObjectTemplateBuilder; // Non-template base class to share code between templates instances. class GIN_EXPORT WrappableBase { protected: WrappableBase(); virtual ~WrappableBase(); + + virtual ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate); + 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, WrappableBase>& data); + v8::Persistent<v8::Object> wrapper_; // Weak + DISALLOW_COPY_AND_ASSIGN(WrappableBase); }; @@ -84,14 +99,14 @@ class Wrappable : public WrappableBase { // This converter handles any subclass of Wrappable. template<typename T> struct Converter<T*, typename base::enable_if< - base::is_convertible<T*, Wrappable<T>*>::value>::type> { + base::is_convertible<T*, WrappableBase*>::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) { - *out = static_cast<T*>(internal::FromV8Impl(isolate, val, - &T::kWrapperInfo)); + *out = static_cast<T*>(static_cast<WrappableBase*>( + internal::FromV8Impl(isolate, val, &T::kWrapperInfo))); return *out != NULL; } }; diff --git a/chromium/gin/wrappable_unittest.cc b/chromium/gin/wrappable_unittest.cc index 3499eedcc18..4916153be52 100644 --- a/chromium/gin/wrappable_unittest.cc +++ b/chromium/gin/wrappable_unittest.cc @@ -15,7 +15,19 @@ namespace gin { -class MyObject : public Wrappable<MyObject> { +class BaseClass { + public: + BaseClass() : value_(23) {} + virtual ~BaseClass() {} + + private: + int value_; + + DISALLOW_COPY_AND_ASSIGN(BaseClass); +}; + +class MyObject : public BaseClass, + public Wrappable<MyObject> { public: static WrapperInfo kWrapperInfo; @@ -26,13 +38,72 @@ class MyObject : public Wrappable<MyObject> { int value() const { return value_; } void set_value(int value) { value_ = value; } - private: + protected: MyObject() : value_(0) {} + virtual ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) OVERRIDE; virtual ~MyObject() {} + private: int value_; }; +class MyObjectSubclass : public MyObject { + public: + static gin::Handle<MyObjectSubclass> Create(v8::Isolate* isolate) { + return CreateHandle(isolate, new MyObjectSubclass()); + } + + void SayHello(const std::string& name) { + result = std::string("Hello, ") + name; + } + + std::string result; + + private: + virtual ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) OVERRIDE { + return MyObject::GetObjectTemplateBuilder(isolate) + .SetMethod("sayHello", &MyObjectSubclass::SayHello); + } + + MyObjectSubclass() { + } + + virtual ~MyObjectSubclass() { + } +}; + +class MyCallableObject : public Wrappable<MyCallableObject> { + public: + static WrapperInfo kWrapperInfo; + + static gin::Handle<MyCallableObject> Create(v8::Isolate* isolate) { + return CreateHandle(isolate, new MyCallableObject()); + } + + int result() { return result_; } + + private: + virtual ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) OVERRIDE { + return Wrappable<MyCallableObject>::GetObjectTemplateBuilder(isolate) + .SetCallAsFunctionHandler(&MyCallableObject::Call); + } + + MyCallableObject() : result_(0) { + } + + virtual ~MyCallableObject() { + } + + void Call(int val) { + result_ = val; + } + + int result_; +}; + class MyObject2 : public Wrappable<MyObject2> { public: static WrapperInfo kWrapperInfo; @@ -44,35 +115,21 @@ class MyObjectBlink : public Wrappable<MyObjectBlink> { }; WrapperInfo MyObject::kWrapperInfo = { kEmbedderNativeGin }; +ObjectTemplateBuilder MyObject::GetObjectTemplateBuilder(v8::Isolate* isolate) { + return Wrappable<MyObject>::GetObjectTemplateBuilder(isolate) + .SetProperty("value", &MyObject::value, &MyObject::set_value); +} + +WrapperInfo MyCallableObject::kWrapperInfo = { kEmbedderNativeGin }; WrapperInfo MyObject2::kWrapperInfo = { kEmbedderNativeGin }; WrapperInfo MyObjectBlink::kWrapperInfo = { kEmbedderNativeGin }; -void RegisterTemplates(v8::Isolate* isolate) { - PerIsolateData* data = PerIsolateData::From(isolate); - DCHECK(data->GetObjectTemplate(&MyObject::kWrapperInfo).IsEmpty()); - - v8::Handle<v8::ObjectTemplate> templ = ObjectTemplateBuilder(isolate) - .SetProperty("value", &MyObject::value, &MyObject::set_value) - .Build(); - templ->SetInternalFieldCount(kNumberOfInternalFields); - data->SetObjectTemplate(&MyObject::kWrapperInfo, templ); - - templ = v8::ObjectTemplate::New(isolate); - templ->SetInternalFieldCount(kNumberOfInternalFields); - data->SetObjectTemplate(&MyObject2::kWrapperInfo, templ); - - templ = v8::ObjectTemplate::New(isolate); - templ->SetInternalFieldCount(kNumberOfInternalFields); - data->SetObjectTemplate(&MyObjectBlink::kWrapperInfo, templ); -} - typedef V8Test WrappableTest; TEST_F(WrappableTest, WrapAndUnwrap) { v8::Isolate* isolate = instance_->isolate(); v8::HandleScope handle_scope(isolate); - RegisterTemplates(isolate); Handle<MyObject> obj = MyObject::Create(isolate); v8::Handle<v8::Value> wrapper = ConvertToV8(isolate, obj.get()); @@ -87,10 +144,8 @@ TEST_F(WrappableTest, UnwrapFailures) { v8::Isolate* isolate = instance_->isolate(); v8::HandleScope handle_scope(isolate); - RegisterTemplates(isolate); - // Something that isn't an object. - v8::Handle<v8::Value> thing = v8::Number::New(42); + v8::Handle<v8::Value> thing = v8::Number::New(isolate, 42); MyObject* unwrapped = NULL; EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped)); EXPECT_FALSE(unwrapped); @@ -117,7 +172,6 @@ TEST_F(WrappableTest, GetAndSetProperty) { v8::Isolate* isolate = instance_->isolate(); v8::HandleScope handle_scope(isolate); - RegisterTemplates(isolate); gin::Handle<MyObject> obj = MyObject::Create(isolate); obj->set_value(42); @@ -130,7 +184,7 @@ TEST_F(WrappableTest, GetAndSetProperty) { EXPECT_FALSE(source.IsEmpty()); gin::TryCatch try_catch; - v8::Handle<v8::Script> script = v8::Script::New(source); + v8::Handle<v8::Script> script = v8::Script::Compile(source); EXPECT_FALSE(script.IsEmpty()); v8::Handle<v8::Value> val = script->Run(); EXPECT_FALSE(val.IsEmpty()); @@ -146,4 +200,71 @@ TEST_F(WrappableTest, GetAndSetProperty) { EXPECT_EQ(191, obj->value()); } +TEST_F(WrappableTest, WrappableSubclass) { + v8::Isolate* isolate = instance_->isolate(); + v8::HandleScope handle_scope(isolate); + + gin::Handle<MyObjectSubclass> object(MyObjectSubclass::Create(isolate)); + v8::Handle<v8::String> source = StringToV8(isolate, + "(function(obj) {" + "obj.sayHello('Lily');" + "})"); + gin::TryCatch try_catch; + v8::Handle<v8::Script> script = v8::Script::Compile(source); + v8::Handle<v8::Value> val = script->Run(); + v8::Handle<v8::Function> func; + EXPECT_TRUE(ConvertFromV8(isolate, val, &func)); + v8::Handle<v8::Value> argv[] = { + ConvertToV8(isolate, object.get()) + }; + func->Call(v8::Undefined(isolate), 1, argv); + EXPECT_FALSE(try_catch.HasCaught()); + EXPECT_EQ("Hello, Lily", object->result); +} + +TEST_F(WrappableTest, ErrorInObjectConstructorProperty) { + v8::Isolate* isolate = instance_->isolate(); + v8::HandleScope handle_scope(isolate); + + v8::Handle<v8::String> source = StringToV8( + isolate, + "(function() {" + " Object.defineProperty(Object.prototype, 'constructor', {" + " get: function() { throw 'Error'; }," + " set: function() { throw 'Error'; }" + " });" + "})();"); + EXPECT_FALSE(source.IsEmpty()); + v8::Handle<v8::Script> script = v8::Script::Compile(source); + script->Run(); + + gin::TryCatch try_catch; + gin::Handle<MyObject> obj = MyObject::Create(isolate); + EXPECT_TRUE(obj.IsEmpty()); + EXPECT_TRUE(try_catch.HasCaught()); +} + +TEST_F(WrappableTest, CallAsFunction) { + v8::Isolate* isolate = instance_->isolate(); + v8::HandleScope handle_scope(isolate); + + gin::Handle<MyCallableObject> object(MyCallableObject::Create(isolate)); + EXPECT_EQ(0, object->result()); + v8::Handle<v8::String> source = StringToV8(isolate, + "(function(obj) {" + "obj(42);" + "})"); + gin::TryCatch try_catch; + v8::Handle<v8::Script> script = v8::Script::Compile(source); + v8::Handle<v8::Value> val = script->Run(); + v8::Handle<v8::Function> func; + EXPECT_TRUE(ConvertFromV8(isolate, val, &func)); + v8::Handle<v8::Value> argv[] = { + ConvertToV8(isolate, object.get()) + }; + func->Call(v8::Undefined(isolate), 1, argv); + EXPECT_FALSE(try_catch.HasCaught()); + EXPECT_EQ(42, object->result()); +} + } // namespace gin |