Revved to chromium 290d2cfa5187ac1f2c87fde4c6dd6f27dad0c93a refs/remotes/origin/HEAD
diff --git a/gin/BUILD.gn b/gin/BUILD.gn
new file mode 100644
index 0000000..b352c3a
--- /dev/null
+++ b/gin/BUILD.gn
@@ -0,0 +1,129 @@
+# 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",
+ "debug_impl.cc",
+ "debug_impl.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/debug.h",
+ "public/gin_embedders.h",
+ "public/isolate_holder.h",
+ "public/v8_platform.h",
+ "public/wrapper_info.h",
+ "runner.cc",
+ "runner.h",
+ "run_microtasks_observer.cc",
+ "run_microtasks_observer.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" ]
+
+ public_deps = [
+ "//base",
+ "//v8",
+ ]
+ deps = [
+ "//base/third_party/dynamic_annotations",
+ ]
+}
+
+executable("gin_shell") {
+ sources = [
+ "shell/gin_main.cc",
+ ]
+
+ deps = [
+ ":gin",
+ "//base",
+ "//base:i18n",
+ "//v8",
+ ]
+}
+
+source_set("gin_test") {
+ testonly = true
+ sources = [
+ "test/file.cc",
+ "test/file.h",
+ "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",
+ ]
+
+ public_deps = [
+ ":gin",
+ "//testing/gtest",
+ ]
+ deps = [
+ "//v8",
+ ]
+}
+
+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",
+ "shell/gin_shell_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/gin/DEPS b/gin/DEPS
new file mode 100644
index 0000000..4e3f30a
--- /dev/null
+++ b/gin/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+base",
+ "+v8",
+]
diff --git a/gin/OWNERS b/gin/OWNERS
new file mode 100644
index 0000000..35dde41
--- /dev/null
+++ b/gin/OWNERS
@@ -0,0 +1,3 @@
+aa@chromium.org
+abarth@chromium.org
+jochen@chromium.org
diff --git a/gin/README b/gin/README
new file mode 100644
index 0000000..fc2d92e
--- /dev/null
+++ b/gin/README
@@ -0,0 +1,24 @@
+Gin - Lightweight bindings for 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/gin/arguments.cc b/gin/arguments.cc
new file mode 100644
index 0000000..6703fc2
--- /dev/null
+++ b/gin/arguments.cc
@@ -0,0 +1,52 @@
+// 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/arguments.h"
+
+#include "base/strings/stringprintf.h"
+#include "gin/converter.h"
+
+namespace gin {
+
+Arguments::Arguments()
+ : isolate_(NULL),
+ info_(NULL),
+ next_(0),
+ insufficient_arguments_(false) {
+}
+
+Arguments::Arguments(const v8::FunctionCallbackInfo<v8::Value>& info)
+ : isolate_(info.GetIsolate()),
+ info_(&info),
+ next_(0),
+ insufficient_arguments_(false) {
+}
+
+Arguments::~Arguments() {
+}
+
+v8::Handle<v8::Value> Arguments::PeekNext() const {
+ if (next_ >= info_->Length())
+ return v8::Handle<v8::Value>();
+ return (*info_)[next_];
+}
+
+void Arguments::ThrowError() const {
+ if (insufficient_arguments_)
+ return ThrowTypeError("Insufficient number of arguments.");
+
+ ThrowTypeError(base::StringPrintf(
+ "Error processing argument %d.", next_ - 1));
+}
+
+void Arguments::ThrowTypeError(const std::string& message) const {
+ isolate_->ThrowException(v8::Exception::TypeError(
+ StringToV8(isolate_, message)));
+}
+
+bool Arguments::IsConstructCall() const {
+ return info_->IsConstructCall();
+}
+
+} // namespace gin
diff --git a/gin/arguments.h b/gin/arguments.h
new file mode 100644
index 0000000..96a3701
--- /dev/null
+++ b/gin/arguments.h
@@ -0,0 +1,95 @@
+// 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_ARGUMENTS_H_
+#define GIN_ARGUMENTS_H_
+
+#include "base/basictypes.h"
+#include "gin/converter.h"
+#include "gin/gin_export.h"
+
+namespace gin {
+
+// Arguments is a wrapper around v8::FunctionCallbackInfo that integrates
+// with Converter to make it easier to marshall arguments and return values
+// between V8 and C++.
+class GIN_EXPORT Arguments {
+ public:
+ Arguments();
+ explicit Arguments(const v8::FunctionCallbackInfo<v8::Value>& info);
+ ~Arguments();
+
+ template<typename T>
+ bool GetHolder(T* out) {
+ return ConvertFromV8(isolate_, info_->Holder(), out);
+ }
+
+ template<typename T>
+ bool GetData(T* out) {
+ return ConvertFromV8(isolate_, info_->Data(), out);
+ }
+
+ template<typename T>
+ bool GetNext(T* out) {
+ if (next_ >= info_->Length()) {
+ insufficient_arguments_ = true;
+ return false;
+ }
+ v8::Handle<v8::Value> val = (*info_)[next_++];
+ return ConvertFromV8(isolate_, val, out);
+ }
+
+ template<typename T>
+ bool GetRemaining(std::vector<T>* out) {
+ if (next_ >= info_->Length()) {
+ insufficient_arguments_ = true;
+ return false;
+ }
+ int remaining = info_->Length() - next_;
+ out->resize(remaining);
+ for (int i = 0; i < remaining; ++i) {
+ v8::Handle<v8::Value> val = (*info_)[next_++];
+ if (!ConvertFromV8(isolate_, val, &out->at(i)))
+ return false;
+ }
+ 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));
+ }
+
+ v8::Handle<v8::Value> PeekNext() const;
+
+ void ThrowError() const;
+ void ThrowTypeError(const std::string& message) const;
+
+ v8::Isolate* isolate() const { return isolate_; }
+
+ // Allows the function handler to distinguish between normal invocation
+ // and object construction.
+ bool IsConstructCall() const;
+
+ private:
+ v8::Isolate* isolate_;
+ const v8::FunctionCallbackInfo<v8::Value>* info_;
+ int next_;
+ bool insufficient_arguments_;
+};
+
+} // namespace gin
+
+#endif // GIN_ARGUMENTS_H_
diff --git a/gin/array_buffer.cc b/gin/array_buffer.cc
new file mode 100644
index 0000000..b777402
--- /dev/null
+++ b/gin/array_buffer.cc
@@ -0,0 +1,197 @@
+// 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 <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);
+
+// ArrayBufferAllocator -------------------------------------------------------
+
+void* ArrayBufferAllocator::Allocate(size_t length) {
+ return calloc(1, length);
+}
+
+void* ArrayBufferAllocator::AllocateUninitialized(size_t length) {
+ return malloc(length);
+}
+
+void ArrayBufferAllocator::Free(void* data, size_t length) {
+ free(data);
+}
+
+ArrayBufferAllocator* ArrayBufferAllocator::SharedInstance() {
+ static ArrayBufferAllocator* instance = new ArrayBufferAllocator();
+ return instance;
+}
+
+// ArrayBuffer::Private -------------------------------------------------------
+
+// This class exists to solve a tricky lifetime problem. The V8 API doesn't
+// want to expose a direct view into the memory behind an array buffer because
+// V8 might deallocate that memory during garbage collection. Instead, the V8
+// API forces us to externalize the buffer and take ownership of the memory.
+// In order to know when to free the memory, we need to figure out both when
+// we're done with it and when V8 is done with it.
+//
+// To determine whether we're done with the memory, every view we have into
+// the array buffer takes a reference to the ArrayBuffer::Private object that
+// actually owns the memory. To determine when V8 is done with the memory, we
+// open a weak handle to the ArrayBuffer object. When we receive the weak
+// callback, we know the object is about to be garbage collected and we can
+// drop V8's implied reference to the memory.
+//
+// The final subtlety is that we need every ArrayBuffer into the same array
+// buffer to AddRef the same ArrayBuffer::Private. To make that work, we store
+// a pointer to the ArrayBuffer::Private object in an internal field of the
+// ArrayBuffer object.
+//
+class ArrayBuffer::Private : public base::RefCounted<ArrayBuffer::Private> {
+ public:
+ static scoped_refptr<Private> From(v8::Isolate* isolate,
+ v8::Handle<v8::ArrayBuffer> array);
+
+ void* buffer() const { return buffer_; }
+ size_t length() const { return length_; }
+
+ private:
+ friend class base::RefCounted<Private>;
+
+ Private(v8::Isolate* isolate, v8::Handle<v8::ArrayBuffer> array);
+ ~Private();
+
+ static void WeakCallback(
+ const v8::WeakCallbackData<v8::ArrayBuffer, Private>& data);
+
+ v8::Persistent<v8::ArrayBuffer> array_buffer_;
+ scoped_refptr<Private> self_reference_;
+ v8::Isolate* isolate_;
+ void* buffer_;
+ size_t length_;
+};
+
+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(kEncodedValueIndex)));
+ }
+ return make_scoped_refptr(new Private(isolate, array));
+}
+
+ArrayBuffer::Private::Private(v8::Isolate* isolate,
+ v8::Handle<v8::ArrayBuffer> 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(kWrapperInfoIndex,
+ &g_array_buffer_wrapper_info);
+ array->SetAlignedPointerInInternalField(kEncodedValueIndex, this);
+
+ self_reference_ = this; // Cleared in WeakCallback.
+ array_buffer_.SetWeak(this, WeakCallback);
+}
+
+ArrayBuffer::Private::~Private() {
+ PerIsolateData::From(isolate_)->allocator()->Free(buffer_, length_);
+}
+
+void ArrayBuffer::Private::WeakCallback(
+ const v8::WeakCallbackData<v8::ArrayBuffer, Private>& data) {
+ Private* parameter = data.GetParameter();
+ parameter->array_buffer_.Reset();
+ parameter->self_reference_ = NULL;
+}
+
+// ArrayBuffer ----------------------------------------------------------------
+
+ArrayBuffer::ArrayBuffer()
+ : bytes_(0),
+ num_bytes_(0) {
+}
+
+ArrayBuffer::ArrayBuffer(v8::Isolate* isolate,
+ v8::Handle<v8::ArrayBuffer> array) {
+ private_ = ArrayBuffer::Private::From(isolate, array);
+ bytes_ = private_->buffer();
+ num_bytes_ = private_->length();
+}
+
+ArrayBuffer::~ArrayBuffer() {
+}
+
+ArrayBuffer& ArrayBuffer::operator=(const ArrayBuffer& other) {
+ private_ = other.private_;
+ bytes_ = other.bytes_;
+ num_bytes_ = other.num_bytes_;
+ return *this;
+}
+
+// Converter<ArrayBuffer> -----------------------------------------------------
+
+bool Converter<ArrayBuffer>::FromV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val,
+ ArrayBuffer* out) {
+ if (!val->IsArrayBuffer())
+ return false;
+ *out = ArrayBuffer(isolate, v8::Handle<v8::ArrayBuffer>::Cast(val));
+ return true;
+}
+
+// ArrayBufferView ------------------------------------------------------------
+
+ArrayBufferView::ArrayBufferView()
+ : offset_(0),
+ num_bytes_(0) {
+}
+
+ArrayBufferView::ArrayBufferView(v8::Isolate* isolate,
+ v8::Handle<v8::ArrayBufferView> view)
+ : array_buffer_(isolate, view->Buffer()),
+ offset_(view->ByteOffset()),
+ num_bytes_(view->ByteLength()) {
+}
+
+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,
+ v8::Handle<v8::Value> val,
+ ArrayBufferView* out) {
+ if (!val->IsArrayBufferView())
+ return false;
+ *out = ArrayBufferView(isolate, v8::Handle<v8::ArrayBufferView>::Cast(val));
+ return true;
+}
+
+} // namespace gin
diff --git a/gin/array_buffer.h b/gin/array_buffer.h
new file mode 100644
index 0000000..048a35f
--- /dev/null
+++ b/gin/array_buffer.h
@@ -0,0 +1,80 @@
+// 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_ARRAY_BUFFER_H_
+#define GIN_ARRAY_BUFFER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "gin/converter.h"
+#include "gin/gin_export.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
+ public:
+ virtual void* Allocate(size_t length) override;
+ virtual void* AllocateUninitialized(size_t length) override;
+ virtual void Free(void* data, size_t length) override;
+
+ GIN_EXPORT static ArrayBufferAllocator* SharedInstance();
+};
+
+class GIN_EXPORT ArrayBuffer {
+ public:
+ ArrayBuffer();
+ ArrayBuffer(v8::Isolate* isolate, v8::Handle<v8::ArrayBuffer> buffer);
+ ~ArrayBuffer();
+ ArrayBuffer& operator=(const ArrayBuffer& other);
+
+ void* bytes() const { return bytes_; }
+ size_t num_bytes() const { return num_bytes_; }
+
+ private:
+ class Private;
+
+ scoped_refptr<Private> private_;
+ void* bytes_;
+ size_t num_bytes_;
+
+ DISALLOW_COPY(ArrayBuffer);
+};
+
+template<>
+struct GIN_EXPORT Converter<ArrayBuffer> {
+ static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val,
+ ArrayBuffer* out);
+};
+
+class GIN_EXPORT ArrayBufferView {
+ public:
+ 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_;
+ }
+ size_t num_bytes() const { return num_bytes_; }
+
+ private:
+ ArrayBuffer array_buffer_;
+ size_t offset_;
+ size_t num_bytes_;
+
+ DISALLOW_COPY(ArrayBufferView);
+};
+
+template<>
+struct GIN_EXPORT Converter<ArrayBufferView> {
+ static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val,
+ ArrayBufferView* out);
+};
+
+} // namespace gin
+
+#endif // GIN_ARRAY_BUFFER_H_
diff --git a/gin/context_holder.cc b/gin/context_holder.cc
new file mode 100644
index 0000000..241b256
--- /dev/null
+++ b/gin/context_holder.cc
@@ -0,0 +1,27 @@
+// 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/public/context_holder.h"
+
+#include "base/logging.h"
+#include "gin/per_context_data.h"
+
+namespace gin {
+
+ContextHolder::ContextHolder(v8::Isolate* isolate)
+ : isolate_(isolate) {
+}
+
+ContextHolder::~ContextHolder() {
+ // PerContextData needs to be destroyed before the context.
+ data_.reset();
+}
+
+void ContextHolder::SetContext(v8::Handle<v8::Context> context) {
+ DCHECK(context_.IsEmpty());
+ context_.Reset(isolate_, context);
+ data_.reset(new PerContextData(this, context));
+}
+
+} // namespace gin
diff --git a/gin/converter.cc b/gin/converter.cc
new file mode 100644
index 0000000..07437b7
--- /dev/null
+++ b/gin/converter.cc
@@ -0,0 +1,205 @@
+// 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/converter.h"
+
+#include "v8/include/v8.h"
+
+using v8::ArrayBuffer;
+using v8::Boolean;
+using v8::External;
+using v8::Function;
+using v8::Handle;
+using v8::Integer;
+using v8::Isolate;
+using v8::Number;
+using v8::Object;
+using v8::String;
+using v8::Value;
+
+namespace gin {
+
+Handle<Value> Converter<bool>::ToV8(Isolate* isolate, bool val) {
+ return Boolean::New(isolate, val).As<Value>();
+}
+
+bool Converter<bool>::FromV8(Isolate* isolate, Handle<Value> val, bool* out) {
+ *out = val->BooleanValue();
+ return true;
+}
+
+Handle<Value> Converter<int32_t>::ToV8(Isolate* isolate, int32_t val) {
+ return Integer::New(isolate, val).As<Value>();
+}
+
+bool Converter<int32_t>::FromV8(Isolate* isolate, Handle<Value> val,
+ int32_t* out) {
+ if (!val->IsInt32())
+ return false;
+ *out = val->Int32Value();
+ return true;
+}
+
+Handle<Value> Converter<uint32_t>::ToV8(Isolate* isolate, uint32_t val) {
+ return Integer::NewFromUnsigned(isolate, val).As<Value>();
+}
+
+bool Converter<uint32_t>::FromV8(Isolate* isolate, Handle<Value> val,
+ uint32_t* out) {
+ if (!val->IsUint32())
+ return false;
+ *out = val->Uint32Value();
+ return true;
+}
+
+Handle<Value> Converter<int64_t>::ToV8(Isolate* isolate, int64_t val) {
+ return Number::New(isolate, static_cast<double>(val)).As<Value>();
+}
+
+bool Converter<int64_t>::FromV8(Isolate* isolate, Handle<Value> val,
+ int64_t* out) {
+ if (!val->IsNumber())
+ return false;
+ // Even though IntegerValue returns int64_t, JavaScript cannot represent
+ // the full precision of int64_t, which means some rounding might occur.
+ *out = val->IntegerValue();
+ return true;
+}
+
+Handle<Value> Converter<uint64_t>::ToV8(Isolate* isolate, uint64_t val) {
+ return Number::New(isolate, static_cast<double>(val)).As<Value>();
+}
+
+bool Converter<uint64_t>::FromV8(Isolate* isolate, Handle<Value> val,
+ uint64_t* out) {
+ if (!val->IsNumber())
+ return false;
+ *out = static_cast<uint64_t>(val->IntegerValue());
+ 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>();
+}
+
+bool Converter<double>::FromV8(Isolate* isolate, Handle<Value> val,
+ double* out) {
+ if (!val->IsNumber())
+ return false;
+ *out = val->NumberValue();
+ return true;
+}
+
+Handle<Value> Converter<base::StringPiece>::ToV8(
+ Isolate* isolate, const base::StringPiece& val) {
+ return String::NewFromUtf8(isolate, val.data(), String::kNormalString,
+ static_cast<uint32_t>(val.length()));
+}
+
+Handle<Value> Converter<std::string>::ToV8(Isolate* isolate,
+ const std::string& val) {
+ return Converter<base::StringPiece>::ToV8(isolate, val);
+}
+
+bool Converter<std::string>::FromV8(Isolate* isolate, Handle<Value> val,
+ std::string* out) {
+ if (!val->IsString())
+ return false;
+ Handle<String> str = Handle<String>::Cast(val);
+ int length = str->Utf8Length();
+ out->resize(length);
+ str->WriteUtf8(&(*out)[0], length, NULL, String::NO_NULL_TERMINATION);
+ return true;
+}
+
+bool Converter<Handle<Function> >::FromV8(Isolate* isolate, Handle<Value> val,
+ Handle<Function>* out) {
+ if (!val->IsFunction())
+ return false;
+ *out = Handle<Function>::Cast(val);
+ return true;
+}
+
+Handle<Value> Converter<Handle<Object> >::ToV8(Isolate* isolate,
+ Handle<Object> val) {
+ return val.As<Value>();
+}
+
+bool Converter<Handle<Object> >::FromV8(Isolate* isolate, Handle<Value> val,
+ Handle<Object>* out) {
+ if (!val->IsObject())
+ return false;
+ *out = Handle<Object>::Cast(val);
+ return true;
+}
+
+Handle<Value> Converter<Handle<ArrayBuffer> >::ToV8(Isolate* isolate,
+ Handle<ArrayBuffer> val) {
+ return val.As<Value>();
+}
+
+bool Converter<Handle<ArrayBuffer> >::FromV8(Isolate* isolate,
+ Handle<Value> val,
+ Handle<ArrayBuffer>* out) {
+ if (!val->IsArrayBuffer())
+ return false;
+ *out = Handle<ArrayBuffer>::Cast(val);
+ return true;
+}
+
+Handle<Value> Converter<Handle<External> >::ToV8(Isolate* isolate,
+ Handle<External> val) {
+ return val.As<Value>();
+}
+
+bool Converter<Handle<External> >::FromV8(Isolate* isolate,
+ v8::Handle<Value> val,
+ Handle<External>* out) {
+ if (!val->IsExternal())
+ return false;
+ *out = Handle<External>::Cast(val);
+ return true;
+}
+
+Handle<Value> Converter<Handle<Value> >::ToV8(Isolate* isolate,
+ Handle<Value> val) {
+ return val;
+}
+
+bool Converter<Handle<Value> >::FromV8(Isolate* isolate, Handle<Value> val,
+ Handle<Value>* out) {
+ *out = val;
+ return true;
+}
+
+v8::Handle<v8::String> StringToSymbol(v8::Isolate* isolate,
+ const base::StringPiece& val) {
+ return String::NewFromUtf8(isolate,
+ val.data(),
+ String::kInternalizedString,
+ static_cast<uint32_t>(val.length()));
+}
+
+std::string V8ToString(v8::Handle<v8::Value> value) {
+ if (value.IsEmpty())
+ return std::string();
+ std::string result;
+ if (!ConvertFromV8(NULL, value, &result))
+ return std::string();
+ return result;
+}
+
+} // namespace gin
diff --git a/gin/converter.h b/gin/converter.h
new file mode 100644
index 0000000..e5c95fc
--- /dev/null
+++ b/gin/converter.h
@@ -0,0 +1,202 @@
+// 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_CONVERTER_H_
+#define GIN_CONVERTER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "gin/gin_export.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+template<typename T, typename Enable = void>
+struct Converter {};
+
+template<>
+struct GIN_EXPORT Converter<bool> {
+ static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+ bool val);
+ static bool FromV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val,
+ bool* out);
+};
+
+template<>
+struct GIN_EXPORT Converter<int32_t> {
+ static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+ int32_t val);
+ static bool FromV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val,
+ int32_t* out);
+};
+
+template<>
+struct GIN_EXPORT Converter<uint32_t> {
+ static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+ uint32_t val);
+ static bool FromV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val,
+ uint32_t* out);
+};
+
+template<>
+struct GIN_EXPORT Converter<int64_t> {
+ // Warning: JavaScript cannot represent 64 integers precisely.
+ static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+ int64_t val);
+ static bool FromV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val,
+ int64_t* out);
+};
+
+template<>
+struct GIN_EXPORT Converter<uint64_t> {
+ // Warning: JavaScript cannot represent 64 integers precisely.
+ static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+ uint64_t val);
+ static bool FromV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val,
+ uint64_t* out);
+};
+
+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);
+ static bool FromV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val,
+ double* out);
+};
+
+template<>
+struct GIN_EXPORT Converter<base::StringPiece> {
+ static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+ const base::StringPiece& val);
+ // No conversion out is possible because StringPiece does not contain storage.
+};
+
+template<>
+struct GIN_EXPORT Converter<std::string> {
+ static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+ const std::string& val);
+ static bool FromV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val,
+ std::string* out);
+};
+
+template<>
+struct GIN_EXPORT Converter<v8::Handle<v8::Function> > {
+ static bool FromV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val,
+ v8::Handle<v8::Function>* out);
+};
+
+template<>
+struct GIN_EXPORT Converter<v8::Handle<v8::Object> > {
+ static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+ v8::Handle<v8::Object> val);
+ static bool FromV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val,
+ v8::Handle<v8::Object>* out);
+};
+
+template<>
+struct GIN_EXPORT Converter<v8::Handle<v8::ArrayBuffer> > {
+ static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+ v8::Handle<v8::ArrayBuffer> val);
+ static bool FromV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val,
+ v8::Handle<v8::ArrayBuffer>* out);
+};
+
+template<>
+struct GIN_EXPORT Converter<v8::Handle<v8::External> > {
+ static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+ v8::Handle<v8::External> val);
+ static bool FromV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val,
+ v8::Handle<v8::External>* out);
+};
+
+template<>
+struct GIN_EXPORT Converter<v8::Handle<v8::Value> > {
+ static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val);
+ static bool FromV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val,
+ v8::Handle<v8::Value>* out);
+};
+
+template<typename T>
+struct Converter<std::vector<T> > {
+ static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+ const std::vector<T>& val) {
+ v8::Handle<v8::Array> result(
+ v8::Array::New(isolate, static_cast<int>(val.size())));
+ for (size_t i = 0; i < val.size(); ++i) {
+ result->Set(static_cast<int>(i), Converter<T>::ToV8(isolate, val[i]));
+ }
+ return result;
+ }
+
+ static bool FromV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val,
+ std::vector<T>* out) {
+ if (!val->IsArray())
+ return false;
+
+ std::vector<T> result;
+ v8::Handle<v8::Array> array(v8::Handle<v8::Array>::Cast(val));
+ uint32_t length = array->Length();
+ for (uint32_t i = 0; i < length; ++i) {
+ T item;
+ if (!Converter<T>::FromV8(isolate, array->Get(i), &item))
+ return false;
+ result.push_back(item);
+ }
+
+ out->swap(result);
+ return true;
+ }
+};
+
+// Convenience functions that deduce T.
+template<typename T>
+v8::Handle<v8::Value> ConvertToV8(v8::Isolate* isolate, T input) {
+ return Converter<T>::ToV8(isolate, input);
+}
+
+GIN_EXPORT inline v8::Handle<v8::String> StringToV8(
+ v8::Isolate* isolate,
+ const base::StringPiece& input) {
+ return ConvertToV8(isolate, input).As<v8::String>();
+}
+
+GIN_EXPORT v8::Handle<v8::String> StringToSymbol(v8::Isolate* isolate,
+ const base::StringPiece& val);
+
+template<typename T>
+bool ConvertFromV8(v8::Isolate* isolate, v8::Handle<v8::Value> input,
+ T* result) {
+ return Converter<T>::FromV8(isolate, input, result);
+}
+
+GIN_EXPORT std::string V8ToString(v8::Handle<v8::Value> value);
+
+} // namespace gin
+
+#endif // GIN_CONVERTER_H_
diff --git a/gin/converter_unittest.cc b/gin/converter_unittest.cc
new file mode 100644
index 0000000..791d7e6
--- /dev/null
+++ b/gin/converter_unittest.cc
@@ -0,0 +1,134 @@
+// 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/converter.h"
+
+#include <limits.h>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "gin/public/isolate_holder.h"
+#include "gin/test/v8_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "v8/include/v8.h"
+
+using v8::Array;
+using v8::Boolean;
+using v8::Handle;
+using v8::HandleScope;
+using v8::Integer;
+using v8::Null;
+using v8::Number;
+using v8::Object;
+using v8::String;
+using v8::Undefined;
+using v8::Value;
+
+namespace gin {
+
+typedef V8Test ConverterTest;
+
+TEST_F(ConverterTest, Bool) {
+ HandleScope handle_scope(instance_->isolate());
+
+ EXPECT_TRUE(Converter<bool>::ToV8(instance_->isolate(), true)->StrictEquals(
+ Boolean::New(instance_->isolate(), true)));
+ EXPECT_TRUE(Converter<bool>::ToV8(instance_->isolate(), false)->StrictEquals(
+ Boolean::New(instance_->isolate(), false)));
+
+ struct {
+ Handle<Value> input;
+ bool expected;
+ } test_data[] = {
+ { Boolean::New(instance_->isolate(), false).As<Value>(), false },
+ { Boolean::New(instance_->isolate(), true).As<Value>(), true },
+ { Number::New(instance_->isolate(), 0).As<Value>(), false },
+ { Number::New(instance_->isolate(), 1).As<Value>(), true },
+ { Number::New(instance_->isolate(), -1).As<Value>(), true },
+ { Number::New(instance_->isolate(), 0.1).As<Value>(), true },
+ { String::NewFromUtf8(instance_->isolate(), "").As<Value>(), false },
+ { String::NewFromUtf8(instance_->isolate(), "foo").As<Value>(), true },
+ { Object::New(instance_->isolate()).As<Value>(), true },
+ { Null(instance_->isolate()).As<Value>(), false },
+ { Undefined(instance_->isolate()).As<Value>(), false },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
+ bool result = false;
+ EXPECT_TRUE(Converter<bool>::FromV8(instance_->isolate(),
+ test_data[i].input, &result));
+ EXPECT_EQ(test_data[i].expected, result);
+
+ result = true;
+ EXPECT_TRUE(Converter<bool>::FromV8(instance_->isolate(),
+ test_data[i].input, &result));
+ EXPECT_EQ(test_data[i].expected, result);
+ }
+}
+
+TEST_F(ConverterTest, Int32) {
+ HandleScope handle_scope(instance_->isolate());
+
+ 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(instance_->isolate(), test_data_to[i])));
+ }
+
+ struct {
+ v8::Handle<v8::Value> input;
+ bool expect_sucess;
+ int expected_result;
+ } test_data_from[] = {
+ { Boolean::New(instance_->isolate(), false).As<Value>(), false, 0 },
+ { Boolean::New(instance_->isolate(), true).As<Value>(), false, 0 },
+ { Integer::New(instance_->isolate(), -1).As<Value>(), true, -1 },
+ { Integer::New(instance_->isolate(), 0).As<Value>(), true, 0 },
+ { Integer::New(instance_->isolate(), 1).As<Value>(), true, 1 },
+ { Number::New(instance_->isolate(), -1).As<Value>(), true, -1 },
+ { Number::New(instance_->isolate(), 1.1).As<Value>(), false, 0 },
+ { String::NewFromUtf8(instance_->isolate(), "42").As<Value>(), false, 0 },
+ { String::NewFromUtf8(instance_->isolate(), "foo").As<Value>(), false, 0 },
+ { Object::New(instance_->isolate()).As<Value>(), false, 0 },
+ { Array::New(instance_->isolate()).As<Value>(), false, 0 },
+ { v8::Null(instance_->isolate()).As<Value>(), false, 0 },
+ { v8::Undefined(instance_->isolate()).As<Value>(), false, 0 },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data_from); ++i) {
+ int32_t result = std::numeric_limits<int32_t>::min();
+ bool success = Converter<int32_t>::FromV8(instance_->isolate(),
+ test_data_from[i].input, &result);
+ EXPECT_EQ(test_data_from[i].expect_sucess, success) << i;
+ if (success)
+ EXPECT_EQ(test_data_from[i].expected_result, result) << i;
+ }
+}
+
+TEST_F(ConverterTest, Vector) {
+ HandleScope handle_scope(instance_->isolate());
+
+ std::vector<int> expected;
+ expected.push_back(-1);
+ expected.push_back(0);
+ expected.push_back(1);
+
+ Handle<Array> js_array = Handle<Array>::Cast(
+ Converter<std::vector<int> >::ToV8(instance_->isolate(), expected));
+ ASSERT_FALSE(js_array.IsEmpty());
+ EXPECT_EQ(3u, js_array->Length());
+ for (size_t i = 0; i < expected.size(); ++i) {
+ EXPECT_TRUE(Integer::New(instance_->isolate(), expected[i])
+ ->StrictEquals(js_array->Get(static_cast<int>(i))));
+ }
+
+ std::vector<int> actual;
+ EXPECT_TRUE(Converter<std::vector<int> >::FromV8(instance_->isolate(),
+ js_array, &actual));
+ EXPECT_EQ(expected, actual);
+}
+
+} // namespace gin
diff --git a/gin/debug_impl.cc b/gin/debug_impl.cc
new file mode 100644
index 0000000..5657ec3
--- /dev/null
+++ b/gin/debug_impl.cc
@@ -0,0 +1,62 @@
+// 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/debug_impl.h"
+
+namespace gin {
+
+namespace {
+v8::FunctionEntryHook g_entry_hook = NULL;
+v8::JitCodeEventHandler g_jit_code_event_handler = NULL;
+#if defined(OS_WIN)
+Debug::CodeRangeCreatedCallback g_code_range_created_callback = NULL;
+Debug::CodeRangeDeletedCallback g_code_range_deleted_callback = NULL;
+#endif
+} // namespace
+
+// static
+void Debug::SetFunctionEntryHook(v8::FunctionEntryHook entry_hook) {
+ g_entry_hook = entry_hook;
+}
+
+// static
+void Debug::SetJitCodeEventHandler(v8::JitCodeEventHandler event_handler) {
+ g_jit_code_event_handler = event_handler;
+}
+
+#if defined(OS_WIN)
+// static
+void Debug::SetCodeRangeCreatedCallback(CodeRangeCreatedCallback callback) {
+ g_code_range_created_callback = callback;
+}
+
+// static
+void Debug::SetCodeRangeDeletedCallback(CodeRangeDeletedCallback callback) {
+ g_code_range_deleted_callback = callback;
+}
+#endif
+
+// static
+v8::FunctionEntryHook DebugImpl::GetFunctionEntryHook() {
+ return g_entry_hook;
+}
+
+// static
+v8::JitCodeEventHandler DebugImpl::GetJitCodeEventHandler() {
+ return g_jit_code_event_handler;
+}
+
+#if defined(OS_WIN)
+// static
+Debug::CodeRangeCreatedCallback DebugImpl::GetCodeRangeCreatedCallback() {
+ return g_code_range_created_callback;
+}
+
+// static
+Debug::CodeRangeDeletedCallback DebugImpl::GetCodeRangeDeletedCallback() {
+ return g_code_range_deleted_callback;
+}
+#endif
+
+} // namespace gin
diff --git a/gin/debug_impl.h b/gin/debug_impl.h
new file mode 100644
index 0000000..96d48ad
--- /dev/null
+++ b/gin/debug_impl.h
@@ -0,0 +1,25 @@
+// 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_DEBUG_IMPL_H_
+#define GIN_PUBLIC_DEBUG_IMPL_H_
+
+#include "gin/public/debug.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+class DebugImpl {
+ public:
+ static v8::FunctionEntryHook GetFunctionEntryHook();
+ static v8::JitCodeEventHandler GetJitCodeEventHandler();
+#if defined(OS_WIN)
+ static Debug::CodeRangeCreatedCallback GetCodeRangeCreatedCallback();
+ static Debug::CodeRangeDeletedCallback GetCodeRangeDeletedCallback();
+#endif
+};
+
+} // namespace gin
+
+#endif // GIN_PUBLIC_DEBUG_IMPL_H_
diff --git a/gin/dictionary.cc b/gin/dictionary.cc
new file mode 100644
index 0000000..d336122
--- /dev/null
+++ b/gin/dictionary.cc
@@ -0,0 +1,42 @@
+// 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/dictionary.h"
+
+namespace gin {
+
+Dictionary::Dictionary(v8::Isolate* isolate)
+ : isolate_(isolate) {
+}
+
+Dictionary::Dictionary(v8::Isolate* isolate,
+ v8::Handle<v8::Object> object)
+ : isolate_(isolate),
+ object_(object) {
+}
+
+Dictionary::~Dictionary() {
+}
+
+Dictionary Dictionary::CreateEmpty(v8::Isolate* isolate) {
+ Dictionary dictionary(isolate);
+ dictionary.object_ = v8::Object::New(isolate);
+ return dictionary;
+}
+
+v8::Handle<v8::Value> Converter<Dictionary>::ToV8(v8::Isolate* isolate,
+ Dictionary val) {
+ return val.object_;
+}
+
+bool Converter<Dictionary>::FromV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val,
+ Dictionary* out) {
+ if (!val->IsObject())
+ return false;
+ *out = Dictionary(isolate, v8::Handle<v8::Object>::Cast(val));
+ return true;
+}
+
+} // namespace gin
diff --git a/gin/dictionary.h b/gin/dictionary.h
new file mode 100644
index 0000000..972108f
--- /dev/null
+++ b/gin/dictionary.h
@@ -0,0 +1,65 @@
+// 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_DICTIONARY_H_
+#define GIN_DICTIONARY_H_
+
+#include "gin/converter.h"
+#include "gin/gin_export.h"
+
+namespace gin {
+
+// Dictionary is useful when writing bindings for a function that either
+// receives an arbitrary JavaScript object as an argument or returns an
+// arbitrary JavaScript object as a result. For example, Dictionary is useful
+// when you might use the |dictionary| type in WebIDL:
+//
+// http://heycam.github.io/webidl/#idl-dictionaries
+//
+// WARNING: You cannot retain a Dictionary object in the heap. The underlying
+// storage for Dictionary is tied to the closest enclosing
+// v8::HandleScope. Generally speaking, you should store a Dictionary
+// on the stack.
+//
+class GIN_EXPORT Dictionary {
+ public:
+ explicit Dictionary(v8::Isolate* isolate);
+ Dictionary(v8::Isolate* isolate, v8::Handle<v8::Object> object);
+ ~Dictionary();
+
+ static Dictionary CreateEmpty(v8::Isolate* isolate);
+
+ template<typename T>
+ bool Get(const std::string& key, T* out) {
+ v8::Handle<v8::Value> val = object_->Get(StringToV8(isolate_, key));
+ return ConvertFromV8(isolate_, val, out);
+ }
+
+ template<typename T>
+ bool Set(const std::string& key, T val) {
+ return object_->Set(StringToV8(isolate_, key), ConvertToV8(isolate_, val));
+ }
+
+ v8::Isolate* isolate() const { return isolate_; }
+
+ private:
+ friend struct Converter<Dictionary>;
+
+ // TODO(aa): Remove this. Instead, get via FromV8(), Set(), and Get().
+ v8::Isolate* isolate_;
+ v8::Handle<v8::Object> object_;
+};
+
+template<>
+struct GIN_EXPORT Converter<Dictionary> {
+ static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+ Dictionary val);
+ static bool FromV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val,
+ Dictionary* out);
+};
+
+} // namespace gin
+
+#endif // GIN_DICTIONARY_H_
diff --git a/gin/function_template.cc b/gin/function_template.cc
new file mode 100644
index 0000000..f76a05b
--- /dev/null
+++ b/gin/function_template.cc
@@ -0,0 +1,33 @@
+// 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/function_template.h"
+
+namespace gin {
+
+namespace internal {
+
+CallbackHolderBase::CallbackHolderBase(v8::Isolate* isolate)
+ : v8_ref_(isolate, v8::External::New(isolate, this)) {
+ v8_ref_.SetWeak(this, &CallbackHolderBase::WeakCallback);
+}
+
+CallbackHolderBase::~CallbackHolderBase() {
+ DCHECK(v8_ref_.IsEmpty());
+}
+
+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/gin/function_template.h b/gin/function_template.h
new file mode 100644
index 0000000..7ba54b5
--- /dev/null
+++ b/gin/function_template.h
@@ -0,0 +1,520 @@
+// This file was GENERATED by command:
+// pump.py function_template.h.pump
+// DO NOT EDIT BY HAND!!!
+
+
+
+#ifndef GIN_FUNCTION_TEMPLATE_H_
+#define GIN_FUNCTION_TEMPLATE_H_
+
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "gin/arguments.h"
+#include "gin/converter.h"
+#include "gin/gin_export.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+class PerIsolateData;
+
+enum CreateFunctionTemplateFlags {
+ HolderIsFirstArgument = 1 << 0,
+};
+
+namespace internal {
+
+template<typename T>
+struct CallbackParamTraits {
+ typedef T LocalType;
+};
+template<typename T>
+struct CallbackParamTraits<const T&> {
+ typedef T LocalType;
+};
+template<typename T>
+struct CallbackParamTraits<const T*> {
+ typedef T* LocalType;
+};
+
+
+// CallbackHolder and CallbackHolderBase are used to pass a base::Callback from
+// CreateFunctionTemplate through v8 (via v8::FunctionTemplate) to
+// DispatchToCallback, where it is invoked.
+
+// This simple base class is used so that we can share a single object template
+// among every CallbackHolder instance.
+class GIN_EXPORT CallbackHolderBase {
+ public:
+ v8::Handle<v8::External> GetHandle(v8::Isolate* isolate);
+
+ protected:
+ 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(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);
+};
+
+
+// This set of templates invokes a base::Callback, converts the return type to a
+// JavaScript value, and returns that value to script via the provided
+// gin::Arguments object.
+//
+// In C++, you can declare the function foo(void), but you can't pass a void
+// 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 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,
+ const P3& a3,
+ const P4& a4) {
+ args->Return(callback.Run(a1, a2, a3, a4));
+ }
+};
+template<typename P1, typename P2, typename P3, typename 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,
+ const P1& a1,
+ const P2& a2,
+ const P3& a3,
+ const P4& a4) {
+ callback.Run(a1, a2, a3, a4);
+ }
+};
+
+template<typename R, typename P1, typename P2, typename P3>
+struct Invoker<R, P1, P2, P3, void, void, void> {
+ inline static void Go(
+ Arguments* args,
+ const base::Callback<R(P1, P2, P3)>& callback,
+ const P1& a1,
+ const P2& a2,
+ const P3& a3) {
+ args->Return(callback.Run(a1, a2, a3));
+ }
+};
+template<typename P1, typename P2, typename P3>
+struct Invoker<void, P1, P2, P3, void, void, void> {
+ inline static void Go(
+ Arguments* args,
+ const base::Callback<void(P1, P2, P3)>& callback,
+ const P1& a1,
+ const P2& a2,
+ const P3& a3) {
+ callback.Run(a1, a2, a3);
+ }
+};
+
+template<typename R, typename P1, typename P2>
+struct Invoker<R, P1, P2, void, void, void, void> {
+ inline static void Go(
+ Arguments* args,
+ const base::Callback<R(P1, P2)>& callback,
+ const P1& a1,
+ const P2& a2) {
+ args->Return(callback.Run(a1, a2));
+ }
+};
+template<typename P1, typename P2>
+struct Invoker<void, P1, P2, void, void, void, void> {
+ inline static void Go(
+ Arguments* args,
+ const base::Callback<void(P1, P2)>& callback,
+ const P1& a1,
+ const P2& a2) {
+ callback.Run(a1, a2);
+ }
+};
+
+template<typename R, typename P1>
+struct Invoker<R, P1, void, void, void, void, void> {
+ inline static void Go(
+ Arguments* args,
+ const base::Callback<R(P1)>& callback,
+ const P1& a1) {
+ args->Return(callback.Run(a1));
+ }
+};
+template<typename P1>
+struct Invoker<void, P1, void, void, void, void, void> {
+ inline static void Go(
+ Arguments* args,
+ const base::Callback<void(P1)>& callback,
+ const P1& a1) {
+ callback.Run(a1);
+ }
+};
+
+template<typename R>
+struct Invoker<R, void, void, void, void, void, void> {
+ inline static void Go(
+ Arguments* args,
+ const base::Callback<R()>& callback) {
+ args->Return(callback.Run());
+ }
+};
+template<>
+struct Invoker<void, void, void, void, void, void, void> {
+ inline static void Go(
+ Arguments* args,
+ const base::Callback<void()>& callback) {
+ callback.Run();
+ }
+};
+
+
+template<typename T>
+bool GetNextArgument(Arguments* args, int create_flags, bool is_first,
+ T* result) {
+ if (is_first && (create_flags & HolderIsFirstArgument) != 0) {
+ return args->GetHolder(result);
+ } else {
+ return args->GetNext(result);
+ }
+}
+
+// For advanced use cases, we allow callers to request the unparsed Arguments
+// object and poke around in it directly.
+inline bool GetNextArgument(Arguments* args, int create_flags, bool is_first,
+ Arguments* result) {
+ *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
+// invokes the base::Callback.
+template<typename Sig>
+struct Dispatcher {
+};
+
+template<typename R>
+struct Dispatcher<R()> {
+ 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()> HolderT;
+ HolderT* holder = static_cast<HolderT*>(holder_base);
+
+ Invoker<R>::Go(&args, holder->callback);
+ }
+};
+
+template<typename R, typename P1>
+struct Dispatcher<R(P1)> {
+ 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)> HolderT;
+ HolderT* holder = static_cast<HolderT*>(holder_base);
+
+ typename CallbackParamTraits<P1>::LocalType a1;
+ if (!GetNextArgument(&args, holder->flags, true, &a1)) {
+ args.ThrowError();
+ return;
+ }
+
+ Invoker<R, P1>::Go(&args, holder->callback, a1);
+ }
+};
+
+template<typename R, typename P1, typename P2>
+struct Dispatcher<R(P1, P2)> {
+ 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)> HolderT;
+ HolderT* holder = static_cast<HolderT*>(holder_base);
+
+ typename CallbackParamTraits<P1>::LocalType a1;
+ typename CallbackParamTraits<P2>::LocalType a2;
+ if (!GetNextArgument(&args, holder->flags, true, &a1) ||
+ !GetNextArgument(&args, holder->flags, false, &a2)) {
+ args.ThrowError();
+ return;
+ }
+
+ Invoker<R, P1, P2>::Go(&args, holder->callback, a1, a2);
+ }
+};
+
+template<typename R, typename P1, typename P2, typename P3>
+struct Dispatcher<R(P1, P2, P3)> {
+ 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)> HolderT;
+ HolderT* holder = static_cast<HolderT*>(holder_base);
+
+ typename CallbackParamTraits<P1>::LocalType a1;
+ typename CallbackParamTraits<P2>::LocalType a2;
+ typename CallbackParamTraits<P3>::LocalType a3;
+ if (!GetNextArgument(&args, holder->flags, true, &a1) ||
+ !GetNextArgument(&args, holder->flags, false, &a2) ||
+ !GetNextArgument(&args, holder->flags, false, &a3)) {
+ args.ThrowError();
+ return;
+ }
+
+ Invoker<R, P1, P2, P3>::Go(&args, holder->callback, a1, a2, a3);
+ }
+};
+
+template<typename R, typename P1, typename P2, typename P3, typename P4>
+struct Dispatcher<R(P1, P2, P3, P4)> {
+ 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)> 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;
+ 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)) {
+ args.ThrowError();
+ return;
+ }
+
+ Invoker<R, P1, P2, P3, P4>::Go(&args, holder->callback, a1, a2, a3, a4);
+ }
+};
+
+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);
+
+ 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
+// JavaScript functions that execute a provided C++ function or base::Callback.
+// JavaScript arguments are automatically converted via gin::Converter, as is
+// the return value of the C++ function, if any.
+template<typename Sig>
+v8::Local<v8::FunctionTemplate> CreateFunctionTemplate(
+ v8::Isolate* isolate, const base::Callback<Sig> callback,
+ int callback_flags = 0) {
+ typedef internal::CallbackHolder<Sig> HolderT;
+ HolderT* holder = new HolderT(isolate, callback, callback_flags);
+
+ return v8::FunctionTemplate::New(
+ isolate,
+ &internal::Dispatcher<Sig>::DispatchToCallback,
+ 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
+
+#endif // GIN_FUNCTION_TEMPLATE_H_
diff --git a/gin/function_template.h.pump b/gin/function_template.h.pump
new file mode 100644
index 0000000..20b2821
--- /dev/null
+++ b/gin/function_template.h.pump
@@ -0,0 +1,240 @@
+$$ This is a pump file for generating file templates. Pump is a python
+$$ script that is part of the Google Test suite of utilities. Description
+$$ can be found here:
+$$
+$$ http://code.google.com/p/googletest/wiki/PumpManual
+$$
+
+#ifndef GIN_FUNCTION_TEMPLATE_H_
+#define GIN_FUNCTION_TEMPLATE_H_
+
+$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
+// found in the LICENSE file.
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "gin/arguments.h"
+#include "gin/converter.h"
+#include "gin/gin_export.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+class PerIsolateData;
+
+enum CreateFunctionTemplateFlags {
+ HolderIsFirstArgument = 1 << 0,
+};
+
+namespace internal {
+
+template<typename T>
+struct CallbackParamTraits {
+ typedef T LocalType;
+};
+template<typename T>
+struct CallbackParamTraits<const T&> {
+ typedef T LocalType;
+};
+template<typename T>
+struct CallbackParamTraits<const T*> {
+ typedef T* LocalType;
+};
+
+
+// CallbackHolder and CallbackHolderBase are used to pass a base::Callback from
+// CreateFunctionTemplate through v8 (via v8::FunctionTemplate) to
+// DispatchToCallback, where it is invoked.
+
+// This simple base class is used so that we can share a single object template
+// among every CallbackHolder instance.
+class GIN_EXPORT CallbackHolderBase {
+ public:
+ v8::Handle<v8::External> GetHandle(v8::Isolate* isolate);
+
+ protected:
+ 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(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);
+};
+
+
+// This set of templates invokes a base::Callback, converts the return type to a
+// JavaScript value, and returns that value to script via the provided
+// gin::Arguments object.
+//
+// In C++, you can declare the function foo(void), but you can't pass a void
+// expression to foo. As a result, we must specialize the case of Callbacks that
+// have the void return type.
+
+$range ARITY 0..MAX_ARITY
+$for ARITY [[
+$var INV_ARITY = MAX_ARITY - ARITY
+$range ARG 1..INV_ARITY
+$range VOID INV_ARITY+1..MAX_ARITY
+
+$if ARITY == 0 [[
+template<typename R$for ARG [[, typename P$(ARG) = void]]>
+struct Invoker {
+]] $else [[
+template<typename R$for ARG [[, typename P$(ARG)]]>
+struct Invoker<R$for ARG [[, P$(ARG)]]$for VOID [[, void]]> {
+]]
+
+ inline static void Go(
+ Arguments* args,
+ const base::Callback<R($for ARG , [[P$(ARG)]])>& callback$for ARG [[,
+ const P$(ARG)& a$(ARG)]]) {
+ args->Return(callback.Run($for ARG, [[a$(ARG)]]));
+ }
+};
+template<$for ARG , [[typename P$(ARG)]]>
+struct Invoker<void$for ARG [[, P$(ARG)]]$for VOID [[, void]]> {
+ inline static void Go(
+ Arguments* args,
+ const base::Callback<void($for ARG , [[P$(ARG)]])>& callback$for ARG [[,
+ const P$(ARG)& a$(ARG)]]) {
+ callback.Run($for ARG, [[a$(ARG)]]);
+ }
+};
+
+
+]]
+
+template<typename T>
+bool GetNextArgument(Arguments* args, int create_flags, bool is_first,
+ T* result) {
+ if (is_first && (create_flags & HolderIsFirstArgument) != 0) {
+ return args->GetHolder(result);
+ } else {
+ return args->GetNext(result);
+ }
+}
+
+// For advanced use cases, we allow callers to request the unparsed Arguments
+// object and poke around in it directly.
+inline bool GetNextArgument(Arguments* args, int create_flags, bool is_first,
+ Arguments* result) {
+ *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
+// invokes the base::Callback.
+template<typename Sig>
+struct Dispatcher {
+};
+
+$range ARITY 0..MAX_ARITY
+$for ARITY [[
+$range ARG 1..ARITY
+
+template<typename R$for ARG [[, typename P$(ARG)]]>
+struct Dispatcher<R($for ARG , [[P$(ARG)]])> {
+ 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($for ARG , [[P$(ARG)]])> HolderT;
+ HolderT* holder = static_cast<HolderT*>(holder_base);
+
+$if ARITY != 0 [[
+
+
+$for ARG [[ typename CallbackParamTraits<P$(ARG)>::LocalType a$(ARG);
+
+]]
+ if ($for ARG ||
+ [[!GetNextArgument(&args, holder->flags, $if ARG == 1 [[true]] $else [[false]], &a$(ARG))]]) {
+ args.ThrowError();
+ return;
+ }
+
+]]
+
+ Invoker<R$for ARG [[, P$(ARG)]]>::Go(&args, holder->callback$for ARG [[, a$(ARG)]]);
+ }
+};
+
+]]
+
+} // namespace internal
+
+
+// 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
+// the return value of the C++ function, if any.
+template<typename Sig>
+v8::Local<v8::FunctionTemplate> CreateFunctionTemplate(
+ v8::Isolate* isolate, const base::Callback<Sig> callback,
+ int callback_flags = 0) {
+ typedef internal::CallbackHolder<Sig> HolderT;
+ HolderT* holder = new HolderT(isolate, callback, callback_flags);
+
+ return v8::FunctionTemplate::New(
+ isolate,
+ &internal::Dispatcher<Sig>::DispatchToCallback,
+ 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
+
+#endif // GIN_FUNCTION_TEMPLATE_H_
diff --git a/gin/gin.gyp b/gin/gin.gyp
new file mode 100644
index 0000000..b38dc85
--- /dev/null
+++ b/gin/gin.gyp
@@ -0,0 +1,147 @@
+# 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'gin',
+ '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',
+ '../v8/tools/gyp/v8.gyp:v8',
+ ],
+ 'defines': [
+ 'GIN_IMPLEMENTATION',
+ ],
+ 'sources': [
+ 'arguments.cc',
+ 'arguments.h',
+ 'array_buffer.cc',
+ 'array_buffer.h',
+ 'context_holder.cc',
+ 'converter.cc',
+ 'converter.h',
+ 'debug_impl.cc',
+ 'debug_impl.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/debug.h',
+ 'public/gin_embedders.h',
+ 'public/isolate_holder.h',
+ 'public/v8_platform.h',
+ 'public/wrapper_info.h',
+ 'runner.cc',
+ 'runner.h',
+ 'run_microtasks_observer.cc',
+ 'run_microtasks_observer.h',
+ 'shell_runner.cc',
+ 'shell_runner.h',
+ 'try_catch.cc',
+ 'try_catch.h',
+ 'v8_platform.cc',
+ 'wrappable.cc',
+ 'wrappable.h',
+ 'wrapper_info.cc',
+ ],
+ },
+ {
+ 'target_name': 'gin_shell',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:base_i18n',
+ '../v8/tools/gyp/v8.gyp:v8',
+ 'gin',
+ ],
+ 'sources': [
+ 'shell/gin_main.cc',
+ ],
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'SubSystem': '1', # /SUBSYSTEM:CONSOLE
+ },
+ },
+ },
+ {
+ 'target_name': 'gin_test',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../testing/gtest.gyp:gtest',
+ '../v8/tools/gyp/v8.gyp:v8',
+ 'gin',
+ ],
+ 'export_dependent_settings': [
+ '../testing/gtest.gyp:gtest',
+ 'gin',
+ ],
+ 'sources': [
+ 'test/file.cc',
+ 'test/file.h',
+ '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',
+ ],
+ },
+ {
+ 'target_name': 'gin_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../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',
+ 'shell/gin_shell_unittest.cc',
+ 'test/run_all_unittests.cc',
+ 'test/run_js_tests.cc',
+ 'wrappable_unittest.cc',
+ ],
+ },
+ ],
+}
diff --git a/gin/gin_export.h b/gin/gin_export.h
new file mode 100644
index 0000000..fc4bf9b
--- /dev/null
+++ b/gin/gin_export.h
@@ -0,0 +1,29 @@
+// 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_GIN_EXPORT_H_
+#define GIN_GIN_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(GIN_IMPLEMENTATION)
+#define GIN_EXPORT __declspec(dllexport)
+#else
+#define GIN_EXPORT __declspec(dllimport)
+#endif // defined(GIN_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(GIN_IMPLEMENTATION)
+#define GIN_EXPORT __attribute__((visibility("default")))
+#else
+#define GIN_EXPORT
+#endif // defined(GIN_IMPLEMENTATION)
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define GIN_EXPORT
+#endif
+
+#endif // GIN_GIN_EXPORT_H_
diff --git a/gin/handle.h b/gin/handle.h
new file mode 100644
index 0000000..01db660
--- /dev/null
+++ b/gin/handle.h
@@ -0,0 +1,71 @@
+// 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_HANDLE_H_
+#define GIN_HANDLE_H_
+
+#include "gin/converter.h"
+
+namespace gin {
+
+// You can use gin::Handle on the stack to retain a gin::Wrappable object.
+// Currently we don't have a mechanism for retaining a gin::Wrappable object
+// in the C++ heap because strong references from C++ to V8 can cause memory
+// leaks.
+template<typename T>
+class Handle {
+ public:
+ Handle() : object_(NULL) {}
+
+ Handle(v8::Handle<v8::Value> wrapper, T* object)
+ : wrapper_(wrapper),
+ object_(object) {
+ }
+
+ bool IsEmpty() const { return !object_; }
+
+ void Clear() {
+ wrapper_.Clear();
+ object_ = NULL;
+ }
+
+ T* operator->() const { return object_; }
+ v8::Handle<v8::Value> ToV8() const { return wrapper_; }
+ T* get() const { return object_; }
+
+ private:
+ v8::Handle<v8::Value> wrapper_;
+ T* object_;
+};
+
+template<typename T>
+struct Converter<gin::Handle<T> > {
+ static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+ const gin::Handle<T>& val) {
+ return val.ToV8();
+ }
+ static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val,
+ gin::Handle<T>* out) {
+ T* object = NULL;
+ if (!Converter<T*>::FromV8(isolate, val, &object)) {
+ return false;
+ }
+ *out = gin::Handle<T>(val, object);
+ return true;
+ }
+};
+
+// This function is a convenient way to create a handle from a raw pointer
+// without having to write out the type of the object explicitly.
+template<typename T>
+gin::Handle<T> CreateHandle(v8::Isolate* isolate, T* object) {
+ v8::Handle<v8::Object> wrapper = object->GetWrapper(isolate);
+ if (wrapper.IsEmpty())
+ return gin::Handle<T>();
+ return gin::Handle<T>(wrapper, object);
+}
+
+} // namespace gin
+
+#endif // GIN_HANDLE_H_
diff --git a/gin/interceptor.cc b/gin/interceptor.cc
new file mode 100644
index 0000000..617fd08
--- /dev/null
+++ b/gin/interceptor.cc
@@ -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.
+
+#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>();
+}
+
+bool NamedPropertyInterceptor::SetNamedProperty(v8::Isolate* isolate,
+ const std::string& property,
+ v8::Local<v8::Value> value) {
+ return false;
+}
+
+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>();
+}
+
+bool IndexedPropertyInterceptor::SetIndexedProperty(
+ v8::Isolate* isolate,
+ uint32_t index,
+ v8::Local<v8::Value> value) {
+ return false;
+}
+
+std::vector<uint32_t> IndexedPropertyInterceptor::EnumerateIndexedProperties(
+ v8::Isolate* isolate) {
+ return std::vector<uint32_t>();
+}
+
+} // namespace gin
diff --git a/gin/interceptor.h b/gin/interceptor.h
new file mode 100644
index 0000000..43cb346
--- /dev/null
+++ b/gin/interceptor.h
@@ -0,0 +1,65 @@
+// 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);
+ // Return true if the set was interecepted.
+ virtual bool 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);
+ // Return true if the set was interecepted.
+ virtual bool SetIndexedProperty(v8::Isolate* isolate,
+ uint32_t index,
+ v8::Local<v8::Value> value);
+ virtual std::vector<uint32_t> EnumerateIndexedProperties(
+ v8::Isolate* isolate);
+
+ private:
+ v8::Isolate* isolate_;
+ WrappableBase* base_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedPropertyInterceptor);
+};
+
+} // namespace gin
+
+#endif // GIN_INTERCEPTOR_H_
diff --git a/gin/interceptor_unittest.cc b/gin/interceptor_unittest.cc
new file mode 100644
index 0000000..d267100
--- /dev/null
+++ b/gin/interceptor_unittest.cc
@@ -0,0 +1,196 @@
+// 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"
+#include "v8/include/v8-util.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 GetFunctionTemplate(isolate, "func")->GetFunction();
+ } else {
+ return v8::Local<v8::Value>();
+ }
+ }
+ virtual bool SetNamedProperty(v8::Isolate* isolate,
+ const std::string& property,
+ v8::Local<v8::Value> value) override {
+ if (property == "value") {
+ ConvertFromV8(isolate, value, &value_);
+ return true;
+ }
+ return false;
+ }
+ 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 bool SetIndexedProperty(v8::Isolate* isolate,
+ uint32_t index,
+ v8::Local<v8::Value> value) override {
+ if (index == 0) {
+ ConvertFromV8(isolate, value, &value_);
+ return true;
+ }
+ // Don't allow bypassing the interceptor.
+ return true;
+ }
+ 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),
+ template_cache_(isolate) {}
+ 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;
+ }
+
+ v8::Local<v8::FunctionTemplate> GetFunctionTemplate(v8::Isolate* isolate,
+ const std::string& name) {
+ v8::Local<v8::FunctionTemplate> function_template =
+ template_cache_.Get(name);
+ if (!function_template.IsEmpty())
+ return function_template;
+ function_template = CreateFunctionTemplate(
+ isolate, base::Bind(&MyInterceptor::Call), HolderIsFirstArgument);
+ template_cache_.Set(name, function_template);
+ return function_template;
+ }
+
+ int value_;
+
+ v8::StdPersistentValueMap<std::string, v8::FunctionTemplate> template_cache_;
+
+ DISALLOW_COPY_AND_ASSIGN(MyInterceptor);
+};
+
+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; })");
+}
+
+TEST_F(InterceptorTest, BypassInterceptorAllowed) {
+ RunInterceptorTest(
+ "(function (obj) {"
+ " obj.value = 191 /* make test happy */;"
+ " obj.foo = 23;"
+ " if (obj.foo !== 23) throw 'FAIL'; })");
+}
+
+TEST_F(InterceptorTest, BypassInterceptorForbidden) {
+ RunInterceptorTest(
+ "(function (obj) {"
+ " obj.value = 191 /* make test happy */;"
+ " obj[1] = 23;"
+ " if (obj[1] === 23) throw 'FAIL'; })");
+}
+
+} // namespace gin
diff --git a/gin/isolate_holder.cc b/gin/isolate_holder.cc
new file mode 100644
index 0000000..740e136
--- /dev/null
+++ b/gin/isolate_holder.cc
@@ -0,0 +1,107 @@
+// 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/public/isolate_holder.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/rand_util.h"
+#include "base/sys_info.h"
+#include "gin/array_buffer.h"
+#include "gin/debug_impl.h"
+#include "gin/function_template.h"
+#include "gin/per_isolate_data.h"
+#include "gin/public/v8_platform.h"
+#include "gin/run_microtasks_observer.h"
+
+namespace gin {
+
+namespace {
+
+v8::ArrayBuffer::Allocator* g_array_buffer_allocator = NULL;
+
+bool GenerateEntropy(unsigned char* buffer, size_t amount) {
+ base::RandBytes(buffer, amount);
+ return true;
+}
+
+} // namespace
+
+IsolateHolder::IsolateHolder() {
+ CHECK(g_array_buffer_allocator)
+ << "You need to invoke gin::IsolateHolder::Initialize first";
+ v8::Isolate::CreateParams params;
+ params.entry_hook = DebugImpl::GetFunctionEntryHook();
+ params.code_event_handler = DebugImpl::GetJitCodeEventHandler();
+ params.constraints.ConfigureDefaults(base::SysInfo::AmountOfPhysicalMemory(),
+ base::SysInfo::AmountOfVirtualMemory(),
+ base::SysInfo::NumberOfProcessors());
+ isolate_ = v8::Isolate::New(params);
+ isolate_data_.reset(new PerIsolateData(isolate_, g_array_buffer_allocator));
+#if defined(OS_WIN)
+ {
+ void* code_range;
+ size_t size;
+ isolate_->GetCodeRange(&code_range, &size);
+ Debug::CodeRangeCreatedCallback callback =
+ DebugImpl::GetCodeRangeCreatedCallback();
+ if (code_range && size && callback)
+ callback(code_range, size);
+ }
+#endif
+}
+
+IsolateHolder::~IsolateHolder() {
+ if (task_observer_.get())
+ base::MessageLoop::current()->RemoveTaskObserver(task_observer_.get());
+#if defined(OS_WIN)
+ {
+ void* code_range;
+ size_t size;
+ isolate_->GetCodeRange(&code_range, &size);
+ Debug::CodeRangeDeletedCallback callback =
+ DebugImpl::GetCodeRangeDeletedCallback();
+ if (code_range && callback)
+ callback(code_range);
+ }
+#endif
+ isolate_data_.reset();
+ isolate_->Dispose();
+}
+
+// static
+void IsolateHolder::Initialize(ScriptMode mode,
+ v8::ArrayBuffer::Allocator* allocator) {
+ CHECK(allocator);
+ static bool v8_is_initialized = false;
+ if (v8_is_initialized)
+ return;
+ v8::V8::InitializePlatform(V8Platform::Get());
+ v8::V8::SetArrayBufferAllocator(allocator);
+ g_array_buffer_allocator = allocator;
+ 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();
+ v8_is_initialized = true;
+}
+
+void IsolateHolder::AddRunMicrotasksObserver() {
+ DCHECK(!task_observer_.get());
+ task_observer_.reset(new RunMicrotasksObserver(isolate_));;
+ base::MessageLoop::current()->AddTaskObserver(task_observer_.get());
+}
+
+void IsolateHolder::RemoveRunMicrotasksObserver() {
+ DCHECK(task_observer_.get());
+ base::MessageLoop::current()->RemoveTaskObserver(task_observer_.get());
+ task_observer_.reset();
+}
+
+} // namespace gin
diff --git a/gin/modules/console.cc b/gin/modules/console.cc
new file mode 100644
index 0000000..d172373
--- /dev/null
+++ b/gin/modules/console.cc
@@ -0,0 +1,49 @@
+// 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/console.h"
+
+#include <iostream>
+
+#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"
+
+using v8::ObjectTemplate;
+
+namespace gin {
+
+namespace {
+
+void Log(Arguments* args) {
+ std::vector<std::string> messages;
+ if (!args->GetRemaining(&messages)) {
+ args->ThrowError();
+ return;
+ }
+ std::cout << JoinString(messages, ' ') << std::endl;
+}
+
+WrapperInfo g_wrapper_info = { kEmbedderNativeGin };
+
+} // namespace
+
+const char Console::kModuleName[] = "console";
+
+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 = ObjectTemplateBuilder(isolate)
+ .SetMethod("log", Log)
+ .Build();
+ data->SetObjectTemplate(&g_wrapper_info, templ);
+ }
+ return templ->NewInstance();
+}
+
+} // namespace gin
diff --git a/gin/modules/console.h b/gin/modules/console.h
new file mode 100644
index 0000000..ff8061b
--- /dev/null
+++ b/gin/modules/console.h
@@ -0,0 +1,23 @@
+// 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_CONSOLE_H_
+#define GIN_MODULES_CONSOLE_H_
+
+#include "gin/gin_export.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+// The Console module provides a basic API for printing to stdout. Over time,
+// we'd like to evolve the API to match window.console in browsers.
+class GIN_EXPORT Console {
+ public:
+ static const char kModuleName[];
+ static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+};
+
+} // namespace gin
+
+#endif // GIN_MODULES_CONSOLE_H_
diff --git a/gin/modules/file_module_provider.cc b/gin/modules/file_module_provider.cc
new file mode 100644
index 0000000..b8465ae
--- /dev/null
+++ b/gin/modules/file_module_provider.cc
@@ -0,0 +1,70 @@
+// 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/file_module_provider.h"
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_split.h"
+#include "gin/converter.h"
+
+namespace gin {
+
+namespace {
+
+void AttempToLoadModule(const base::WeakPtr<Runner>& runner,
+ const std::vector<base::FilePath>& search_paths,
+ const std::string& id) {
+ if (!runner)
+ return;
+
+ std::vector<std::string> components;
+ base::SplitString(id, '/', &components);
+
+ base::FilePath path;
+ for (size_t i = 0; i < components.size(); ++i) {
+ // TODO(abarth): Technically the path components can be UTF-8. We don't
+ // handle that case correctly yet.
+ path = path.AppendASCII(components[i]);
+ }
+ path = path.AddExtension(FILE_PATH_LITERAL("js"));
+
+ for (size_t i = 0; i < search_paths.size(); ++i) {
+ std::string source;
+ if (!ReadFileToString(search_paths[i].Append(path), &source))
+ continue;
+
+ Runner::Scope scope(runner.get());
+ runner->Run(source, id);
+ return;
+ }
+ LOG(ERROR) << "Failed to load module from disk: " << id;
+}
+
+} // namespace
+
+FileModuleProvider::FileModuleProvider(
+ const std::vector<base::FilePath>& search_paths)
+ : search_paths_(search_paths) {
+}
+
+FileModuleProvider::~FileModuleProvider() {
+}
+
+void FileModuleProvider::AttempToLoadModules(
+ Runner* runner, const std::set<std::string>& ids) {
+ std::set<std::string> modules = ids;
+ for (std::set<std::string>::const_iterator it = modules.begin();
+ it != modules.end(); ++it) {
+ const std::string& id = *it;
+ if (attempted_ids_.count(id))
+ continue;
+ attempted_ids_.insert(id);
+ base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ AttempToLoadModule, runner->GetWeakPtr(), search_paths_, id));
+ }
+}
+
+} // namespace gin
diff --git a/gin/modules/file_module_provider.h b/gin/modules/file_module_provider.h
new file mode 100644
index 0000000..dd75a0f
--- /dev/null
+++ b/gin/modules/file_module_provider.h
@@ -0,0 +1,44 @@
+// 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_FILE_MODULE_PROVIDER_H_
+#define GIN_MODULES_FILE_MODULE_PROVIDER_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "gin/gin_export.h"
+#include "gin/runner.h"
+
+namespace gin {
+
+// FileModuleProvider knows how to load AMD modules off disk. It searches for
+// modules in the directories indiciated by |search_paths|. Although we still
+// read from the file system on the main thread, we'll eventually want to move
+// the reads to a background thread.
+class GIN_EXPORT FileModuleProvider {
+ public:
+ explicit FileModuleProvider(
+ const std::vector<base::FilePath>& search_paths);
+ ~FileModuleProvider();
+
+ // Searches for modules with |ids| in the file system. If found, the modules
+ // will be executed asynchronously by |runner|.
+ void AttempToLoadModules(Runner* runner, const std::set<std::string>& ids);
+
+ private:
+ std::vector<base::FilePath> search_paths_;
+
+ // We'll only search for a given module once. We remember the set of modules
+ // we've already looked for in |attempted_ids_|.
+ std::set<std::string> attempted_ids_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileModuleProvider);
+};
+
+} // namespace gin
+
+#endif // GIN_MODULES_FILE_MODULE_PROVIDER_H_
diff --git a/gin/modules/module_registry.cc b/gin/modules/module_registry.cc
new file mode 100644
index 0000000..a92a546
--- /dev/null
+++ b/gin/modules/module_registry.cc
@@ -0,0 +1,273 @@
+// 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/module_registry.h"
+
+#include <string>
+#include <vector>
+
+#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"
+
+using v8::Context;
+using v8::External;
+using v8::Function;
+using v8::FunctionTemplate;
+using v8::Isolate;
+using v8::Local;
+using v8::Object;
+using v8::ObjectTemplate;
+using v8::Persistent;
+using v8::StackTrace;
+using v8::String;
+using v8::Value;
+
+namespace gin {
+
+struct PendingModule {
+ PendingModule();
+ ~PendingModule();
+
+ std::string id;
+ std::vector<std::string> dependencies;
+ Persistent<Value> factory;
+};
+
+PendingModule::PendingModule() {
+}
+
+PendingModule::~PendingModule() {
+ factory.Reset();
+}
+
+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);
+
+ if (!info.Length())
+ return args.ThrowTypeError("At least one argument is required.");
+
+ std::string id;
+ std::vector<std::string> dependencies;
+ v8::Handle<Value> factory;
+
+ if (args.PeekNext()->IsString())
+ args.GetNext(&id);
+ if (args.PeekNext()->IsArray())
+ args.GetNext(&dependencies);
+ if (!args.GetNext(&factory))
+ return args.ThrowError();
+
+ scoped_ptr<PendingModule> pending(new PendingModule);
+ pending->id = id;
+ pending->dependencies = dependencies;
+ pending->factory.Reset(args.isolate(), factory);
+
+ ModuleRegistry* registry =
+ ModuleRegistry::From(args.isolate()->GetCurrentContext());
+ registry->AddPendingModule(args.isolate(), pending.Pass());
+}
+
+WrapperInfo g_wrapper_info = { kEmbedderNativeGin };
+
+Local<FunctionTemplate> GetDefineTemplate(Isolate* isolate) {
+ PerIsolateData* data = PerIsolateData::From(isolate);
+ Local<FunctionTemplate> templ = data->GetFunctionTemplate(
+ &g_wrapper_info);
+ if (templ.IsEmpty()) {
+ templ = FunctionTemplate::New(isolate, Define);
+ data->SetFunctionTemplate(&g_wrapper_info, templ);
+ }
+ return templ;
+}
+
+} // namespace
+
+ModuleRegistry::ModuleRegistry(Isolate* isolate)
+ : 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) {
+ 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 registry_data->registry.get();
+}
+
+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, 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,
+ const std::string& id,
+ LoadModuleCallback callback) {
+ if (available_modules_.find(id) != available_modules_.end()) {
+ // Should we call the callback asynchronously?
+ callback.Run(GetModule(isolate, id));
+ return;
+ }
+ waiting_callbacks_.insert(std::make_pair(id, callback));
+ unsatisfied_dependencies_.insert(id);
+}
+
+void ModuleRegistry::RegisterModule(Isolate* isolate,
+ const std::string& id,
+ v8::Handle<Value> module) {
+ if (id.empty() || module.IsEmpty())
+ return;
+
+ unsatisfied_dependencies_.erase(id);
+ available_modules_.insert(id);
+ v8::Handle<Object> modules = Local<Object>::New(isolate, modules_);
+ modules->Set(StringToSymbol(isolate, id), module);
+
+ std::pair<LoadModuleCallbackMap::iterator, LoadModuleCallbackMap::iterator>
+ range = waiting_callbacks_.equal_range(id);
+ std::vector<LoadModuleCallback> callbacks;
+ callbacks.reserve(waiting_callbacks_.count(id));
+ for (LoadModuleCallbackMap::iterator it = range.first; it != range.second;
+ ++it) {
+ callbacks.push_back(it->second);
+ }
+ waiting_callbacks_.erase(range.first, range.second);
+ for (std::vector<LoadModuleCallback>::iterator it = callbacks.begin();
+ it != callbacks.end();
+ ++it) {
+ // Should we call the callback asynchronously?
+ it->Run(module);
+ }
+}
+
+bool ModuleRegistry::CheckDependencies(PendingModule* pending) {
+ size_t num_missing_dependencies = 0;
+ size_t len = pending->dependencies.size();
+ for (size_t i = 0; i < len; ++i) {
+ const std::string& dependency = pending->dependencies[i];
+ if (available_modules_.count(dependency))
+ continue;
+ unsatisfied_dependencies_.insert(dependency);
+ num_missing_dependencies++;
+ }
+ return num_missing_dependencies == 0;
+}
+
+void ModuleRegistry::Load(Isolate* isolate, scoped_ptr<PendingModule> pending) {
+ if (!pending->id.empty() && available_modules_.count(pending->id))
+ return; // We've already loaded this module.
+
+ uint32_t argc = static_cast<uint32_t>(pending->dependencies.size());
+ std::vector<v8::Handle<Value> > argv(argc);
+ for (uint32_t i = 0; i < argc; ++i)
+ argv[i] = GetModule(isolate, pending->dependencies[i]);
+
+ v8::Handle<Value> module = Local<Value>::New(isolate, pending->factory);
+
+ v8::Handle<Function> factory;
+ if (ConvertFromV8(isolate, module, &factory)) {
+ PerContextData* data = PerContextData::From(isolate->GetCurrentContext());
+ Runner* runner = data->runner();
+ module = runner->Call(factory, runner->global(), argc,
+ argv.empty() ? NULL : &argv.front());
+ if (pending->id.empty())
+ ConvertFromV8(isolate, factory->GetScriptOrigin().ResourceName(),
+ &pending->id);
+ }
+
+ RegisterModule(isolate, pending->id, module);
+}
+
+bool ModuleRegistry::AttemptToLoad(Isolate* isolate,
+ scoped_ptr<PendingModule> pending) {
+ if (!CheckDependencies(pending.get())) {
+ pending_modules_.push_back(pending.release());
+ return false;
+ }
+ Load(isolate, pending.Pass());
+ return true;
+}
+
+v8::Handle<v8::Value> ModuleRegistry::GetModule(v8::Isolate* isolate,
+ const std::string& id) {
+ v8::Handle<Object> modules = Local<Object>::New(isolate, modules_);
+ v8::Handle<String> key = StringToSymbol(isolate, id);
+ DCHECK(modules->HasOwnProperty(key));
+ return modules->Get(key);
+}
+
+void ModuleRegistry::AttemptToLoadMoreModules(Isolate* isolate) {
+ bool keep_trying = true;
+ while (keep_trying) {
+ keep_trying = false;
+ PendingModuleVector pending_modules;
+ pending_modules.swap(pending_modules_);
+ for (size_t i = 0; i < pending_modules.size(); ++i) {
+ scoped_ptr<PendingModule> pending(pending_modules[i]);
+ pending_modules[i] = NULL;
+ if (AttemptToLoad(isolate, pending.Pass()))
+ keep_trying = true;
+ }
+ }
+}
+
+} // namespace gin
diff --git a/gin/modules/module_registry.h b/gin/modules/module_registry.h
new file mode 100644
index 0000000..439207e
--- /dev/null
+++ b/gin/modules/module_registry.h
@@ -0,0 +1,109 @@
+// 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_MODULE_REGISTRY_H_
+#define GIN_MODULES_MODULE_REGISTRY_H_
+
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+
+#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 "v8/include/v8.h"
+
+namespace gin {
+
+class ModuleRegistryObserver;
+struct PendingModule;
+
+// This class implements the Asynchronous Module Definition (AMD) API.
+// https://github.com/amdjs/amdjs-api/wiki/AMD
+//
+// Our implementation isn't complete yet. Missing features:
+// 1) Built-in support for require, exports, and module.
+// 2) Path resoltuion in module names.
+//
+// For these reasons, we don't have an "amd" property on the "define"
+// function. The spec says we should only add that property once our
+// implementation complies with the specification.
+//
+class GIN_EXPORT ModuleRegistry {
+ public:
+ typedef base::Callback<void (v8::Handle<v8::Value>)> LoadModuleCallback;
+
+ virtual ~ModuleRegistry();
+
+ static ModuleRegistry* From(v8::Handle<v8::Context> context);
+
+ 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::Value> module);
+
+ // The caller must have already entered our context.
+ void AddPendingModule(v8::Isolate* isolate,
+ scoped_ptr<PendingModule> pending);
+
+ void LoadModule(v8::Isolate* isolate,
+ const std::string& id,
+ LoadModuleCallback callback);
+
+ // The caller must have already entered our context.
+ void AttemptToLoadMoreModules(v8::Isolate* isolate);
+
+ const std::set<std::string>& available_modules() const {
+ return available_modules_;
+ }
+
+ const std::set<std::string>& unsatisfied_dependencies() const {
+ return unsatisfied_dependencies_;
+ }
+
+ private:
+ typedef ScopedVector<PendingModule> PendingModuleVector;
+ typedef std::multimap<std::string, LoadModuleCallback> LoadModuleCallbackMap;
+
+ explicit ModuleRegistry(v8::Isolate* isolate);
+
+ void Load(v8::Isolate* isolate, scoped_ptr<PendingModule> pending);
+ void RegisterModule(v8::Isolate* isolate,
+ const std::string& id,
+ v8::Handle<v8::Value> module);
+
+ bool CheckDependencies(PendingModule* pending);
+ bool AttemptToLoad(v8::Isolate* isolate, scoped_ptr<PendingModule> pending);
+
+ v8::Handle<v8::Value> GetModule(v8::Isolate* isolate, const std::string& id);
+
+ std::set<std::string> available_modules_;
+ std::set<std::string> unsatisfied_dependencies_;
+
+ LoadModuleCallbackMap waiting_callbacks_;
+
+ PendingModuleVector pending_modules_;
+ v8::Persistent<v8::Object> modules_;
+
+ ObserverList<ModuleRegistryObserver> observer_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModuleRegistry);
+};
+
+} // namespace gin
+
+#endif // GIN_MODULES_MODULE_REGISTRY_H_
diff --git a/gin/modules/module_registry_observer.h b/gin/modules/module_registry_observer.h
new file mode 100644
index 0000000..68ee4ad
--- /dev/null
+++ b/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/gin/modules/module_registry_unittest.cc b/gin/modules/module_registry_unittest.cc
new file mode 100644
index 0000000..77bf9f1
--- /dev/null
+++ b/gin/modules/module_registry_unittest.cc
@@ -0,0 +1,136 @@
+// 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/bind.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);
+};
+
+void NestedCallback(v8::Handle<v8::Value> value) {
+ FAIL() << "Should not be called";
+}
+
+void OnModuleLoaded(TestHelper* helper,
+ v8::Isolate* isolate,
+ int64_t* counter,
+ v8::Handle<v8::Value> value) {
+ ASSERT_TRUE(value->IsNumber());
+ v8::Handle<v8::Integer> int_value = v8::Handle<v8::Integer>::Cast(value);
+ *counter += int_value->Value();
+ ModuleRegistry::From(helper->runner->GetContextHolder()->context())
+ ->LoadModule(isolate, "two", base::Bind(NestedCallback));
+}
+
+} // 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]);
+}
+
+// Verifies that multiple LoadModule calls for the same module are handled
+// correctly.
+TEST_F(ModuleRegistryTest, LoadModuleTest) {
+ TestHelper helper(instance_->isolate());
+ int64_t counter = 0;
+ std::string source =
+ "define('one', [], function() {"
+ " return 1;"
+ "});";
+
+ ModuleRegistry::LoadModuleCallback callback =
+ base::Bind(OnModuleLoaded, &helper, instance_->isolate(), &counter);
+ for (int i = 0; i < 3; i++) {
+ ModuleRegistry::From(helper.runner->GetContextHolder()->context())
+ ->LoadModule(instance_->isolate(), "one", callback);
+ }
+ EXPECT_EQ(0, counter);
+ helper.runner->Run(source, "script");
+ EXPECT_EQ(3, counter);
+}
+
+} // namespace gin
diff --git a/gin/modules/module_registry_unittests.js b/gin/modules/module_registry_unittests.js
new file mode 100644
index 0000000..ca70148
--- /dev/null
+++ b/gin/modules/module_registry_unittests.js
@@ -0,0 +1,30 @@
+// 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.
+
+define("module0", function() {
+ return {
+ "foo": "bar",
+ }
+});
+
+define("module2", [
+ "gtest",
+ "module0",
+ "module1"
+ ], function(gtest, module0, module1) {
+ gtest.expectEqual(module0.foo, "bar",
+ "module0.foo is " + module0.foo);
+ gtest.expectFalse(module0.bar,
+ "module0.bar is " + module0.bar);
+ gtest.expectEqual(module1.baz, "qux",
+ "module1.baz is " + module1.baz);
+ gtest.expectFalse(module1.qux,
+ "module1.qux is " + module1.qux);
+
+ this.result = "PASS";
+});
+
+define("module1", {
+ "baz": "qux",
+});
diff --git a/gin/modules/module_runner_delegate.cc b/gin/modules/module_runner_delegate.cc
new file mode 100644
index 0000000..9d8ce2e
--- /dev/null
+++ b/gin/modules/module_runner_delegate.cc
@@ -0,0 +1,67 @@
+// 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/module_runner_delegate.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "gin/modules/module_registry.h"
+#include "gin/object_template_builder.h"
+#include "gin/public/context_holder.h"
+
+namespace gin {
+
+ModuleRunnerDelegate::ModuleRunnerDelegate(
+ const std::vector<base::FilePath>& search_paths)
+ : module_provider_(search_paths) {
+}
+
+ModuleRunnerDelegate::~ModuleRunnerDelegate() {
+}
+
+void ModuleRunnerDelegate::AddBuiltinModule(const std::string& id,
+ ModuleGetter getter) {
+ builtin_modules_[id] = base::Bind(getter);
+}
+
+void ModuleRunnerDelegate::AddBuiltinModule(const std::string& id,
+ const ModuleGetterCallback& getter) {
+ builtin_modules_[id] = getter;
+}
+
+void ModuleRunnerDelegate::AttemptToLoadMoreModules(Runner* runner) {
+ 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(
+ ShellRunner* runner,
+ v8::Isolate* isolate) {
+ v8::Handle<v8::ObjectTemplate> templ = ObjectTemplateBuilder(isolate).Build();
+ ModuleRegistry::RegisterGlobals(isolate, templ);
+ return templ;
+}
+
+void ModuleRunnerDelegate::DidCreateContext(ShellRunner* runner) {
+ ShellRunnerDelegate::DidCreateContext(runner);
+
+ 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(isolate, it->first, it->second.Run(isolate));
+ }
+}
+
+void ModuleRunnerDelegate::DidRunScript(ShellRunner* runner) {
+ AttemptToLoadMoreModules(runner);
+}
+
+} // namespace gin
diff --git a/gin/modules/module_runner_delegate.h b/gin/modules/module_runner_delegate.h
new file mode 100644
index 0000000..df91f63
--- /dev/null
+++ b/gin/modules/module_runner_delegate.h
@@ -0,0 +1,56 @@
+// 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_MODULE_RUNNER_DELEGATE_H_
+#define GIN_MODULES_MODULE_RUNNER_DELEGATE_H_
+
+#include <map>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "gin/gin_export.h"
+#include "gin/modules/file_module_provider.h"
+#include "gin/shell_runner.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+typedef v8::Local<v8::Value> (*ModuleGetter)(v8::Isolate* isolate);
+typedef base::Callback<v8::Local<v8::Value>(v8::Isolate*)> ModuleGetterCallback;
+
+// 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 ShellRunnerDelegate {
+ public:
+ explicit ModuleRunnerDelegate(
+ const std::vector<base::FilePath>& search_paths);
+ virtual ~ModuleRunnerDelegate();
+
+ void AddBuiltinModule(const std::string& id, ModuleGetter getter);
+ void AddBuiltinModule(const std::string& id,
+ const ModuleGetterCallback& getter);
+
+ protected:
+ void AttemptToLoadMoreModules(Runner* runner);
+
+ private:
+ typedef std::map<std::string, ModuleGetterCallback> BuiltinModuleMap;
+
+ // From ShellRunnerDelegate:
+ virtual v8::Handle<v8::ObjectTemplate> GetGlobalTemplate(
+ ShellRunner* runner,
+ v8::Isolate* isolate) OVERRIDE;
+ virtual void DidCreateContext(ShellRunner* runner) OVERRIDE;
+ virtual void DidRunScript(ShellRunner* runner) OVERRIDE;
+
+ BuiltinModuleMap builtin_modules_;
+ FileModuleProvider module_provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModuleRunnerDelegate);
+};
+
+} // namespace gin
+
+#endif // GIN_MODULES_MODULE_RUNNER_DELEGATE_H_
diff --git a/gin/modules/timer.cc b/gin/modules/timer.cc
new file mode 100644
index 0000000..4ba7f91
--- /dev/null
+++ b/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)
+ : timer_(false, repeating),
+ runner_(PerContextData::From(
+ isolate->GetCurrentContext())->runner()->GetWeakPtr()),
+ weak_factory_(this) {
+ 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/gin/modules/timer.h b/gin/modules/timer.h
new file mode 100644
index 0000000..1ec0b93
--- /dev/null
+++ b/gin/modules/timer.h
@@ -0,0 +1,66 @@
+// 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::Timer timer_;
+ base::WeakPtr<gin::Runner> runner_;
+ base::WeakPtrFactory<Timer> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(Timer);
+};
+
+
+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/gin/modules/timer_unittest.cc b/gin/modules/timer_unittest.cc
new file mode 100644
index 0000000..f7fd8f2
--- /dev/null
+++ b/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/gin/object_template_builder.cc b/gin/object_template_builder.cc
new file mode 100644
index 0000000..f649d34
--- /dev/null
+++ b/gin/object_template_builder.cc
@@ -0,0 +1,181 @@
+// 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/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);
+ if (interceptor->SetNamedProperty(isolate, name, value))
+ info.GetReturnValue().Set(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;
+ if (interceptor->SetIndexedProperty(isolate, index, value))
+ info.GetReturnValue().Set(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);
+ return *this;
+}
+
+ObjectTemplateBuilder& ObjectTemplateBuilder::SetPropertyImpl(
+ const base::StringPiece& name, v8::Handle<v8::FunctionTemplate> getter,
+ v8::Handle<v8::FunctionTemplate> setter) {
+ template_->SetAccessorProperty(StringToSymbol(isolate_, name), getter,
+ setter);
+ return *this;
+}
+
+v8::Local<v8::ObjectTemplate> ObjectTemplateBuilder::Build() {
+ v8::Local<v8::ObjectTemplate> result = template_;
+ template_.Clear();
+ return result;
+}
+
+} // namespace gin
diff --git a/gin/object_template_builder.h b/gin/object_template_builder.h
new file mode 100644
index 0000000..3d025a9
--- /dev/null
+++ b/gin/object_template_builder.h
@@ -0,0 +1,147 @@
+// 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_OBJECT_TEMPLATE_BUILDER_H_
+#define GIN_OBJECT_TEMPLATE_BUILDER_H_
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/strings/string_piece.h"
+#include "base/template_util.h"
+#include "gin/converter.h"
+#include "gin/function_template.h"
+#include "gin/gin_export.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+namespace {
+
+// Base template - used only for non-member function pointers. Other types
+// either go to one of the below specializations, or go here and fail to compile
+// because of base::Bind().
+template<typename T, typename Enable = void>
+struct CallbackTraits {
+ static v8::Handle<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate,
+ 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.
+template<typename T>
+struct CallbackTraits<base::Callback<T> > {
+ static v8::Handle<v8::FunctionTemplate> CreateTemplate(
+ 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
+// specially because the first parameter for callbacks to MFP should typically
+// come from the the JavaScript "this" object the function was called on, not
+// from the first normal parameter.
+template<typename T>
+struct CallbackTraits<T, typename base::enable_if<
+ base::is_member_function_pointer<T>::value>::type> {
+ static v8::Handle<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate,
+ T callback) {
+ return CreateFunctionTemplate(isolate, base::Bind(callback),
+ 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
+// they need to do fancier stuff.
+template<>
+struct GIN_EXPORT CallbackTraits<v8::Handle<v8::FunctionTemplate> > {
+ static v8::Handle<v8::FunctionTemplate> CreateTemplate(
+ v8::Handle<v8::FunctionTemplate> templ) {
+ return templ;
+ }
+};
+
+} // namespace
+
+
+// ObjectTemplateBuilder provides a handy interface to creating
+// v8::ObjectTemplate instances with various sorts of properties.
+class GIN_EXPORT ObjectTemplateBuilder {
+ public:
+ explicit ObjectTemplateBuilder(v8::Isolate* isolate);
+ ~ObjectTemplateBuilder();
+
+ // It's against Google C++ style to return a non-const ref, but we take some
+ // poetic license here in order that all calls to Set() can be via the '.'
+ // operator and line up nicely.
+ template<typename T>
+ ObjectTemplateBuilder& SetValue(const base::StringPiece& name, T val) {
+ return SetImpl(name, ConvertToV8(isolate_, val));
+ }
+
+ // In the following methods, T and U can be function pointer, member function
+ // pointer, base::Callback, or v8::FunctionTemplate. Most clients will want to
+ // use one of the first two options. Also see gin::CreateFunctionTemplate()
+ // for creating raw function templates.
+ template<typename T>
+ ObjectTemplateBuilder& SetMethod(const base::StringPiece& name,
+ const T& callback) {
+ return SetImpl(name, CallbackTraits<T>::CreateTemplate(isolate_, callback));
+ }
+ template<typename T>
+ ObjectTemplateBuilder& SetProperty(const base::StringPiece& name,
+ const T& getter) {
+ return SetPropertyImpl(name,
+ CallbackTraits<T>::CreateTemplate(isolate_, getter),
+ v8::Local<v8::FunctionTemplate>());
+ }
+ template<typename T, typename U>
+ ObjectTemplateBuilder& SetProperty(const base::StringPiece& name,
+ const T& getter, const U& setter) {
+ return SetPropertyImpl(name,
+ 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();
+
+ private:
+ ObjectTemplateBuilder& SetImpl(const base::StringPiece& name,
+ v8::Handle<v8::Data> val);
+ ObjectTemplateBuilder& SetPropertyImpl(
+ const base::StringPiece& name, v8::Handle<v8::FunctionTemplate> getter,
+ v8::Handle<v8::FunctionTemplate> setter);
+
+ v8::Isolate* isolate_;
+
+ // ObjectTemplateBuilder should only be used on the stack.
+ v8::Local<v8::ObjectTemplate> template_;
+};
+
+} // namespace gin
+
+#endif // GIN_OBJECT_TEMPLATE_BUILDER_H_
diff --git a/gin/per_context_data.cc b/gin/per_context_data.cc
new file mode 100644
index 0000000..178c0d1
--- /dev/null
+++ b/gin/per_context_data.cc
@@ -0,0 +1,33 @@
+// 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/per_context_data.h"
+
+#include "base/logging.h"
+#include "gin/public/context_holder.h"
+#include "gin/public/wrapper_info.h"
+
+namespace gin {
+
+PerContextData::PerContextData(ContextHolder* context_holder,
+ v8::Handle<v8::Context> context)
+ : context_holder_(context_holder),
+ runner_(NULL) {
+ context->SetAlignedPointerInEmbedderData(
+ kPerContextDataStartIndex + kEmbedderNativeGin, this);
+}
+
+PerContextData::~PerContextData() {
+ v8::HandleScope handle_scope(context_holder_->isolate());
+ context_holder_->context()->SetAlignedPointerInEmbedderData(
+ kPerContextDataStartIndex + kEmbedderNativeGin, NULL);
+}
+
+// static
+PerContextData* PerContextData::From(v8::Handle<v8::Context> context) {
+ return static_cast<PerContextData*>(
+ context->GetAlignedPointerFromEmbedderData(kEncodedValueIndex));
+}
+
+} // namespace gin
diff --git a/gin/per_context_data.h b/gin/per_context_data.h
new file mode 100644
index 0000000..0d11653
--- /dev/null
+++ b/gin/per_context_data.h
@@ -0,0 +1,48 @@
+// 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_PER_CONTEXT_DATA_H_
+#define GIN_PER_CONTEXT_DATA_H_
+
+#include "base/basictypes.h"
+#include "base/supports_user_data.h"
+#include "gin/gin_export.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+class ContextHolder;
+class Runner;
+
+// There is one instance of PerContextData per v8::Context managed by Gin. This
+// 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:
+ 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> 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; }
+
+ ContextHolder* context_holder() { return context_holder_; }
+
+ private:
+ ContextHolder* context_holder_;
+ Runner* runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(PerContextData);
+};
+
+} // namespace gin
+
+#endif // GIN_PER_CONTEXT_DATA_H_
diff --git a/gin/per_context_data_unittest.cc b/gin/per_context_data_unittest.cc
new file mode 100644
index 0000000..4d79587
--- /dev/null
+++ b/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/gin/per_isolate_data.cc b/gin/per_isolate_data.cc
new file mode 100644
index 0000000..99c928c
--- /dev/null
+++ b/gin/per_isolate_data.cc
@@ -0,0 +1,112 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "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;
+using v8::Object;
+using v8::FunctionTemplate;
+using v8::ObjectTemplate;
+
+namespace gin {
+
+PerIsolateData::PerIsolateData(Isolate* isolate,
+ ArrayBuffer::Allocator* allocator)
+ : isolate_(isolate),
+ allocator_(allocator),
+ message_loop_proxy_(base::MessageLoopProxy::current()) {
+ isolate_->SetData(kEmbedderNativeGin, this);
+}
+
+PerIsolateData::~PerIsolateData() {
+ isolate_->SetData(kEmbedderNativeGin, NULL);
+}
+
+PerIsolateData* PerIsolateData::From(Isolate* isolate) {
+ return static_cast<PerIsolateData*>(isolate->GetData(kEmbedderNativeGin));
+}
+
+void PerIsolateData::SetObjectTemplate(WrapperInfo* info,
+ Local<ObjectTemplate> templ) {
+ object_templates_[info] = Eternal<ObjectTemplate>(isolate_, templ);
+}
+
+void PerIsolateData::SetFunctionTemplate(WrapperInfo* info,
+ Local<FunctionTemplate> templ) {
+ function_templates_[info] = Eternal<FunctionTemplate>(isolate_, templ);
+}
+
+v8::Local<v8::ObjectTemplate> PerIsolateData::GetObjectTemplate(
+ WrapperInfo* info) {
+ ObjectTemplateMap::iterator it = object_templates_.find(info);
+ if (it == object_templates_.end())
+ return v8::Local<v8::ObjectTemplate>();
+ return it->second.Get(isolate_);
+}
+
+v8::Local<v8::FunctionTemplate> PerIsolateData::GetFunctionTemplate(
+ WrapperInfo* info) {
+ FunctionTemplateMap::iterator it = function_templates_.find(info);
+ if (it == function_templates_.end())
+ return v8::Local<v8::FunctionTemplate>();
+ return it->second.Get(isolate_);
+}
+
+void PerIsolateData::SetIndexedPropertyInterceptor(
+ WrappableBase* base,
+ IndexedPropertyInterceptor* interceptor) {
+ indexed_interceptors_[base] = interceptor;
+}
+
+void PerIsolateData::SetNamedPropertyInterceptor(
+ WrappableBase* base,
+ NamedPropertyInterceptor* interceptor) {
+ named_interceptors_[base] = interceptor;
+}
+
+void PerIsolateData::ClearIndexedPropertyInterceptor(
+ WrappableBase* base,
+ IndexedPropertyInterceptor* interceptor) {
+ IndexedPropertyInterceptorMap::iterator it = indexed_interceptors_.find(base);
+ if (it != indexed_interceptors_.end())
+ indexed_interceptors_.erase(it);
+ else
+ NOTREACHED();
+}
+
+void PerIsolateData::ClearNamedPropertyInterceptor(
+ WrappableBase* base,
+ NamedPropertyInterceptor* interceptor) {
+ NamedPropertyInterceptorMap::iterator it = named_interceptors_.find(base);
+ if (it != named_interceptors_.end())
+ named_interceptors_.erase(it);
+ else
+ NOTREACHED();
+}
+
+IndexedPropertyInterceptor* PerIsolateData::GetIndexedPropertyInterceptor(
+ WrappableBase* base) {
+ IndexedPropertyInterceptorMap::iterator it = indexed_interceptors_.find(base);
+ if (it != indexed_interceptors_.end())
+ return it->second;
+ else
+ return NULL;
+}
+
+NamedPropertyInterceptor* PerIsolateData::GetNamedPropertyInterceptor(
+ WrappableBase* base) {
+ NamedPropertyInterceptorMap::iterator it = named_interceptors_.find(base);
+ if (it != named_interceptors_.end())
+ return it->second;
+ else
+ return NULL;
+}
+
+} // namespace gin
diff --git a/gin/per_isolate_data.h b/gin/per_isolate_data.h
new file mode 100644
index 0000000..bffe5fb
--- /dev/null
+++ b/gin/per_isolate_data.h
@@ -0,0 +1,97 @@
+// 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_PER_ISOLATE_DATA_H_
+#define GIN_PER_ISOLATE_DATA_H_
+
+#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:
+ PerIsolateData(v8::Isolate* isolate, v8::ArrayBuffer::Allocator* allocator);
+ ~PerIsolateData();
+
+ static PerIsolateData* From(v8::Isolate* isolate);
+
+ // Each isolate is associated with a collection of v8::ObjectTemplates and
+ // v8::FunctionTemplates. Typically these template objects are created
+ // lazily.
+ void SetObjectTemplate(WrapperInfo* info,
+ v8::Local<v8::ObjectTemplate> object_template);
+ void SetFunctionTemplate(WrapperInfo* info,
+ v8::Local<v8::FunctionTemplate> function_template);
+
+ // These are low-level functions for retrieving object or function templates
+ // stored in this object. Because these templates are often created lazily,
+ // most clients should call higher-level functions that know how to populate
+ // these templates if they haven't already been created.
+ 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);
+};
+
+} // namespace gin
+
+#endif // GIN_PER_ISOLATE_DATA_H_
diff --git a/gin/public/context_holder.h b/gin/public/context_holder.h
new file mode 100644
index 0000000..afbcf23
--- /dev/null
+++ b/gin/public/context_holder.h
@@ -0,0 +1,52 @@
+// 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_PUBLIC_CONTEXT_HOLDER_H_
+#define GIN_PUBLIC_CONTEXT_HOLDER_H_
+
+#include <list>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "gin/gin_export.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+// Gin embedder that store embedder data in v8::Contexts must do so in a
+// single field with the index kPerContextDataStartIndex + GinEmbedder-enum.
+// The field at kDebugIdIndex is treated specially by V8 and is reserved for
+// a V8 debugger implementation (not used by gin).
+enum ContextEmbedderDataFields {
+ kDebugIdIndex = 0,
+ kPerContextDataStartIndex,
+};
+
+class PerContextData;
+
+// ContextHolder is a generic class for holding a v8::Context.
+class GIN_EXPORT ContextHolder {
+ public:
+ explicit ContextHolder(v8::Isolate* isolate);
+ ~ContextHolder();
+
+ v8::Isolate* isolate() const { return isolate_; }
+
+ v8::Handle<v8::Context> context() const {
+ return v8::Local<v8::Context>::New(isolate_, context_);
+ }
+
+ void SetContext(v8::Handle<v8::Context> context);
+
+ private:
+ v8::Isolate* isolate_;
+ v8::UniquePersistent<v8::Context> context_;
+ scoped_ptr<PerContextData> data_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContextHolder);
+};
+
+} // namespace gin
+
+#endif // GIN_PUBLIC_CONTEXT_HOLDER_H_
diff --git a/gin/public/debug.h b/gin/public/debug.h
new file mode 100644
index 0000000..0c24109
--- /dev/null
+++ b/gin/public/debug.h
@@ -0,0 +1,57 @@
+// 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_DEBUG_H_
+#define GIN_PUBLIC_DEBUG_H_
+
+#include "build/build_config.h"
+#include "gin/gin_export.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+class GIN_EXPORT Debug {
+ public:
+ /* Installs a callback that is invoked on entry to every V8-generated
+ * function.
+ *
+ * This only affects IsolateHolder instances created after
+ * SetFunctionEntryHook was invoked.
+ */
+ static void SetFunctionEntryHook(v8::FunctionEntryHook entry_hook);
+
+ /* Installs a callback that is invoked each time jit code is added, moved,
+ * or removed.
+ *
+ * This only affects IsolateHolder instances created after
+ * SetJitCodeEventHandler was invoked.
+ */
+ static void SetJitCodeEventHandler(v8::JitCodeEventHandler event_handler);
+
+#if defined(OS_WIN)
+ typedef void (__cdecl *CodeRangeCreatedCallback)(void*, size_t);
+
+ /* Sets a callback that is invoked for every new code range being created.
+ *
+ * On Win64, exception handling in jitted code is broken due to the fact
+ * that JS stack frames are not ABI compliant. It is possible to install
+ * custom handlers for the code range which holds the jitted code to work
+ * around this issue.
+ *
+ * https://code.google.com/p/v8/issues/detail?id=3598
+ */
+ static void SetCodeRangeCreatedCallback(CodeRangeCreatedCallback callback);
+
+ typedef void (__cdecl *CodeRangeDeletedCallback)(void*);
+
+ /* Sets a callback that is invoked for every previously registered code range
+ * when it is deleted.
+ */
+ static void SetCodeRangeDeletedCallback(CodeRangeDeletedCallback callback);
+#endif
+};
+
+} // namespace gin
+
+#endif // GIN_PUBLIC_DEBUG_H_
diff --git a/gin/public/gin_embedders.h b/gin/public/gin_embedders.h
new file mode 100644
index 0000000..bffe6e0
--- /dev/null
+++ b/gin/public/gin_embedders.h
@@ -0,0 +1,21 @@
+// 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_PUBLIC_GIN_EMBEDDERS_H_
+#define GIN_PUBLIC_GIN_EMBEDDERS_H_
+
+namespace gin {
+
+// The GinEmbedder is used to identify the owner of embedder data stored on
+// v8 objects, and is used as in index into the embedder data slots of a
+// v8::Isolate.
+
+enum GinEmbedder {
+ kEmbedderNativeGin,
+ kEmbedderBlink,
+};
+
+} // namespace gin
+
+#endif // GIN_PUBLIC_GIN_EMBEDDERS_H_
diff --git a/gin/public/isolate_holder.h b/gin/public/isolate_holder.h
new file mode 100644
index 0000000..29cc208
--- /dev/null
+++ b/gin/public/isolate_holder.h
@@ -0,0 +1,62 @@
+// 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_PUBLIC_ISOLATE_HOLDER_H_
+#define GIN_PUBLIC_ISOLATE_HOLDER_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "gin/gin_export.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+class PerIsolateData;
+class RunMicrotasksObserver;
+
+// To embed Gin, first initialize gin using IsolateHolder::Initialize and then
+// 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 isolate.
+class GIN_EXPORT IsolateHolder {
+ public:
+ // Controls whether or not V8 should only accept strict mode scripts.
+ enum ScriptMode {
+ kNonStrictMode,
+ kStrictMode
+ };
+
+ IsolateHolder();
+ ~IsolateHolder();
+
+ // Should be invoked once before creating IsolateHolder instances to
+ // initialize V8 and Gin.
+ static void Initialize(ScriptMode mode,
+ v8::ArrayBuffer::Allocator* allocator);
+
+ v8::Isolate* isolate() { return isolate_; }
+
+ // The implementations of Object.observe() and Promise enqueue v8 Microtasks
+ // that should be executed just before control is returned to the message
+ // loop. This method adds a MessageLoop TaskObserver which runs any pending
+ // Microtasks each time a Task is completed. This method should be called
+ // once, when a MessageLoop is created and it should be called on the
+ // MessageLoop's thread.
+ void AddRunMicrotasksObserver();
+
+ // This method should also only be called once, and on the MessageLoop's
+ // thread.
+ void RemoveRunMicrotasksObserver();
+
+ private:
+ v8::Isolate* isolate_;
+ scoped_ptr<PerIsolateData> isolate_data_;
+ scoped_ptr<RunMicrotasksObserver> task_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(IsolateHolder);
+};
+
+} // namespace gin
+
+#endif // GIN_PUBLIC_ISOLATE_HOLDER_H_
diff --git a/gin/public/v8_platform.h b/gin/public/v8_platform.h
new file mode 100644
index 0000000..2df0f84
--- /dev/null
+++ b/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/gin/public/wrapper_info.h b/gin/public/wrapper_info.h
new file mode 100644
index 0000000..31b2a98
--- /dev/null
+++ b/gin/public/wrapper_info.h
@@ -0,0 +1,32 @@
+// 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_PUBLIC_WRAPPER_INFO_H_
+#define GIN_PUBLIC_WRAPPER_INFO_H_
+
+#include "gin/gin_export.h"
+#include "gin/public/gin_embedders.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+// Gin embedder that use their own WrapperInfo-like structs must ensure that
+// the first field is of type GinEmbedderId and has the correct id set. They
+// also should use kWrapperInfoIndex to start their WrapperInfo-like struct
+// and ensure that all objects have kNumberOfInternalFields internal fields.
+
+enum InternalFields {
+ kWrapperInfoIndex,
+ kEncodedValueIndex,
+ kNumberOfInternalFields,
+};
+
+struct GIN_EXPORT WrapperInfo {
+ static WrapperInfo* From(v8::Handle<v8::Object> object);
+ const GinEmbedder embedder;
+};
+
+} // namespace gin
+
+#endif // GIN_PUBLIC_WRAPPER_INFO_H_
diff --git a/gin/run_microtasks_observer.cc b/gin/run_microtasks_observer.cc
new file mode 100644
index 0000000..f453a66
--- /dev/null
+++ b/gin/run_microtasks_observer.cc
@@ -0,0 +1,21 @@
+// 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/run_microtasks_observer.h"
+
+namespace gin {
+
+RunMicrotasksObserver::RunMicrotasksObserver(v8::Isolate* isolate)
+ : isolate_(isolate) {
+}
+
+void RunMicrotasksObserver::WillProcessTask(const base::PendingTask& task) {
+}
+
+void RunMicrotasksObserver::DidProcessTask(const base::PendingTask& task) {
+ v8::Isolate::Scope scope(isolate_);
+ isolate_->RunMicrotasks();
+}
+
+} // namespace gin
diff --git a/gin/run_microtasks_observer.h b/gin/run_microtasks_observer.h
new file mode 100644
index 0000000..d31d804
--- /dev/null
+++ b/gin/run_microtasks_observer.h
@@ -0,0 +1,30 @@
+// 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_RUN_MICROTASKS_OBSERVER_H_
+#define GIN_RUN_MICROTASKS_OBSERVER_H_
+
+#include "base/message_loop/message_loop.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+// Runs any pending v8 Microtasks each time a task is completed.
+// TODO(hansmuller); At some point perhaps this can be replaced with
+// the (currently experimental) Isolate::SetAutorunMicrotasks() method.
+
+class RunMicrotasksObserver : public base::MessageLoop::TaskObserver {
+ public:
+ RunMicrotasksObserver(v8::Isolate* isolate);
+
+ virtual void WillProcessTask(const base::PendingTask& pending_task) override;
+ virtual void DidProcessTask(const base::PendingTask& pending_task) override;
+
+ private:
+ v8::Isolate* isolate_;
+};
+
+} // namespace gin
+
+#endif // GIN_RUN_MICROTASKS_OBSERVER_H_
diff --git a/gin/runner.cc b/gin/runner.cc
new file mode 100644
index 0000000..6f018b1
--- /dev/null
+++ b/gin/runner.cc
@@ -0,0 +1,24 @@
+// 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/runner.h"
+
+namespace gin {
+
+Runner::Runner() : weak_factory_(this) {
+}
+
+Runner::~Runner() {
+}
+
+Runner::Scope::Scope(Runner* runner)
+ : isolate_scope_(runner->GetContextHolder()->isolate()),
+ handle_scope_(runner->GetContextHolder()->isolate()),
+ scope_(runner->GetContextHolder()->context()) {
+}
+
+Runner::Scope::~Scope() {
+}
+
+} // namespace gin
diff --git a/gin/runner.h b/gin/runner.h
new file mode 100644
index 0000000..36a75d2
--- /dev/null
+++ b/gin/runner.h
@@ -0,0 +1,66 @@
+// 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_RUNNER_H_
+#define GIN_RUNNER_H_
+
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "gin/gin_export.h"
+#include "gin/public/context_holder.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+// Runner is responsible for running code in a v8::Context.
+class GIN_EXPORT Runner {
+ public:
+ 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.
+ 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
+ // holding a raw pointer to the runner, consider holding a WeakPtr.
+ base::WeakPtr<Runner> GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+ }
+
+ class GIN_EXPORT Scope {
+ public:
+ explicit Scope(Runner* runner);
+ ~Scope();
+
+ private:
+ v8::Isolate::Scope isolate_scope_;
+ v8::HandleScope handle_scope_;
+ v8::Context::Scope scope_;
+
+ DISALLOW_COPY_AND_ASSIGN(Scope);
+ };
+
+ private:
+ friend class Scope;
+
+ base::WeakPtrFactory<Runner> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(Runner);
+};
+
+} // namespace gin
+
+#endif // GIN_RUNNER_H_
diff --git a/gin/shell/gin_main.cc b/gin/shell/gin_main.cc
new file mode 100644
index 0000000..aa9f2b5
--- /dev/null
+++ b/gin/shell/gin_main.cc
@@ -0,0 +1,88 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/i18n/icu_util.h"
+#include "base/message_loop/message_loop.h"
+#include "gin/array_buffer.h"
+#include "gin/modules/console.h"
+#include "gin/modules/module_runner_delegate.h"
+#include "gin/public/isolate_holder.h"
+#include "gin/test/file_runner.h"
+#include "gin/try_catch.h"
+
+namespace gin {
+namespace {
+
+std::string Load(const base::FilePath& path) {
+ std::string source;
+ if (!ReadFileToString(path, &source))
+ LOG(FATAL) << "Unable to read " << path.LossyDisplayName();
+ return source;
+}
+
+void Run(base::WeakPtr<Runner> runner, const base::FilePath& path) {
+ if (!runner)
+ return;
+ Runner::Scope scope(runner.get());
+ runner->Run(Load(path), path.AsUTF8Unsafe());
+}
+
+std::vector<base::FilePath> GetModuleSearchPaths() {
+ std::vector<base::FilePath> module_base(1);
+ CHECK(base::GetCurrentDirectory(&module_base[0]));
+ return module_base;
+}
+
+class GinShellRunnerDelegate : public ModuleRunnerDelegate {
+ public:
+ GinShellRunnerDelegate() : ModuleRunnerDelegate(GetModuleSearchPaths()) {
+ AddBuiltinModule(Console::kModuleName, Console::GetModule);
+ }
+
+ virtual void UnhandledException(ShellRunner* runner,
+ TryCatch& try_catch) OVERRIDE {
+ ModuleRunnerDelegate::UnhandledException(runner, try_catch);
+ LOG(ERROR) << try_catch.GetStackTrace();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GinShellRunnerDelegate);
+};
+
+} // namespace
+} // namespace gin
+
+int main(int argc, char** argv) {
+ base::AtExitManager at_exit;
+ CommandLine::Init(argc, argv);
+ base::i18n::InitializeICU();
+
+ gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode,
+ gin::ArrayBufferAllocator::SharedInstance());
+ gin::IsolateHolder instance;
+
+ base::MessageLoop message_loop;
+
+ gin::GinShellRunnerDelegate delegate;
+ gin::ShellRunner runner(&delegate, instance.isolate());
+
+ {
+ gin::Runner::Scope scope(&runner);
+ v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
+ }
+
+ CommandLine::StringVector args = CommandLine::ForCurrentProcess()->GetArgs();
+ for (CommandLine::StringVector::const_iterator it = args.begin();
+ it != args.end(); ++it) {
+ base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ gin::Run, runner.GetWeakPtr(), base::FilePath(*it)));
+ }
+
+ message_loop.RunUntilIdle();
+ return 0;
+}
diff --git a/gin/shell/gin_shell_unittest.cc b/gin/shell/gin_shell_unittest.cc
new file mode 100644
index 0000000..50e2ba3
--- /dev/null
+++ b/gin/shell/gin_shell_unittest.cc
@@ -0,0 +1,39 @@
+// 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/command_line.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/process/launch.h"
+#include "base/strings/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+base::FilePath GinShellPath() {
+ base::FilePath dir;
+ PathService::Get(base::DIR_EXE, &dir);
+ return dir.AppendASCII("gin_shell");
+}
+
+base::FilePath HelloWorldPath() {
+ base::FilePath path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ return path
+ .AppendASCII("gin")
+ .AppendASCII("shell")
+ .AppendASCII("hello_world.js");
+}
+
+TEST(GinShellTest, HelloWorld) {
+ base::FilePath gin_shell_path(GinShellPath());
+ base::FilePath hello_world_path(HelloWorldPath());
+ ASSERT_TRUE(base::PathExists(gin_shell_path));
+ ASSERT_TRUE(base::PathExists(hello_world_path));
+
+ CommandLine cmd(gin_shell_path);
+ cmd.AppendArgPath(hello_world_path);
+ std::string output;
+ ASSERT_TRUE(base::GetAppOutput(cmd, &output));
+ base::TrimWhitespaceASCII(output, base::TRIM_ALL, &output);
+ ASSERT_EQ("Hello World", output);
+}
diff --git a/gin/shell/hello_world.js b/gin/shell/hello_world.js
new file mode 100644
index 0000000..7216fbd
--- /dev/null
+++ b/gin/shell/hello_world.js
@@ -0,0 +1,7 @@
+// 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.
+
+define(["console"], function(console) {
+ console.log("Hello World");
+});
diff --git a/gin/shell_runner.cc b/gin/shell_runner.cc
new file mode 100644
index 0000000..8d98e42
--- /dev/null
+++ b/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/gin/shell_runner.h b/gin/shell_runner.h
new file mode 100644
index 0000000..ca88a5d
--- /dev/null
+++ b/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/gin/shell_runner_unittest.cc b/gin/shell_runner_unittest.cc
new file mode 100644
index 0000000..07ab678
--- /dev/null
+++ b/gin/shell_runner_unittest.cc
@@ -0,0 +1,40 @@
+// 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 "base/compiler_specific.h"
+#include "gin/array_buffer.h"
+#include "gin/converter.h"
+#include "gin/public/isolate_holder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using v8::Isolate;
+using v8::Object;
+using v8::Script;
+using v8::String;
+
+namespace gin {
+
+TEST(RunnerTest, Run) {
+ std::string source = "this.result = 'PASS';\n";
+
+ gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode,
+ gin::ArrayBufferAllocator::SharedInstance());
+ gin::IsolateHolder instance;
+
+ ShellRunnerDelegate delegate;
+ Isolate* isolate = instance.isolate();
+ ShellRunner runner(&delegate, isolate);
+ Runner::Scope scope(&runner);
+ runner.Run(source, "test_data.js");
+
+ std::string result;
+ EXPECT_TRUE(Converter<std::string>::FromV8(isolate,
+ runner.global()->Get(StringToV8(isolate, "result")),
+ &result));
+ EXPECT_EQ("PASS", result);
+}
+
+} // namespace gin
diff --git a/gin/test/expect.js b/gin/test/expect.js
new file mode 100644
index 0000000..b5e0f21
--- /dev/null
+++ b/gin/test/expect.js
@@ -0,0 +1,289 @@
+// 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.
+
+define(function() {
+ // Equality function based on isEqual in
+ // Underscore.js 1.5.2
+ // http://underscorejs.org
+ // (c) 2009-2013 Jeremy Ashkenas,
+ // DocumentCloud,
+ // and Investigative Reporters & Editors
+ // Underscore may be freely distributed under the MIT license.
+ //
+ function has(obj, key) {
+ return obj.hasOwnProperty(key);
+ }
+ function isFunction(obj) {
+ return typeof obj === 'function';
+ }
+ function isArrayBufferClass(className) {
+ return className == '[object ArrayBuffer]' ||
+ className.match(/\[object \w+\d+(Clamped)?Array\]/);
+ }
+ // Internal recursive comparison function for `isEqual`.
+ function eq(a, b, aStack, bStack) {
+ // Identical objects are equal. `0 === -0`, but they aren't identical.
+ // See the Harmony `egal` proposal:
+ // http://wiki.ecmascript.org/doku.php?id=harmony:egal.
+ if (a === b)
+ return a !== 0 || 1 / a == 1 / b;
+ // A strict comparison is necessary because `null == undefined`.
+ if (a == null || b == null)
+ return a === b;
+ // Compare `[[Class]]` names.
+ var className = toString.call(a);
+ if (className != toString.call(b))
+ return false;
+ switch (className) {
+ // Strings, numbers, dates, and booleans are compared by value.
+ case '[object String]':
+ // Primitives and their corresponding object wrappers are equivalent;
+ // thus, `"5"` is equivalent to `new String("5")`.
+ return a == String(b);
+ case '[object Number]':
+ // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is
+ // performed for other numeric values.
+ return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
+ case '[object Date]':
+ case '[object Boolean]':
+ // Coerce dates and booleans to numeric primitive values. Dates are
+ // compared by their millisecond representations. Note that invalid
+ // dates with millisecond representations of `NaN` are not equivalent.
+ return +a == +b;
+ // RegExps are compared by their source patterns and flags.
+ case '[object RegExp]':
+ return a.source == b.source &&
+ a.global == b.global &&
+ a.multiline == b.multiline &&
+ a.ignoreCase == b.ignoreCase;
+ }
+ if (typeof a != 'object' || typeof b != 'object')
+ return false;
+ // Assume equality for cyclic structures. The algorithm for detecting
+ // cyclic structures is adapted from ES 5.1 section 15.12.3, abstract
+ // operation `JO`.
+ var length = aStack.length;
+ while (length--) {
+ // Linear search. Performance is inversely proportional to the number of
+ // unique nested structures.
+ if (aStack[length] == a)
+ return bStack[length] == b;
+ }
+ // Objects with different constructors are not equivalent, but `Object`s
+ // from different frames are.
+ var aCtor = a.constructor, bCtor = b.constructor;
+ if (aCtor !== bCtor && !(isFunction(aCtor) && (aCtor instanceof aCtor) &&
+ isFunction(bCtor) && (bCtor instanceof bCtor))
+ && ('constructor' in a && 'constructor' in b)) {
+ return false;
+ }
+ // Add the first object to the stack of traversed objects.
+ aStack.push(a);
+ bStack.push(b);
+ var size = 0, result = true;
+ // Recursively compare objects and arrays.
+ if (className == '[object Array]' || isArrayBufferClass(className)) {
+ // Compare array lengths to determine if a deep comparison is necessary.
+ size = a.length;
+ result = size == b.length;
+ if (result) {
+ // Deep compare the contents, ignoring non-numeric properties.
+ while (size--) {
+ if (!(result = eq(a[size], b[size], aStack, bStack)))
+ break;
+ }
+ }
+ } else {
+ // Deep compare objects.
+ for (var key in a) {
+ if (has(a, key)) {
+ // Count the expected number of properties.
+ size++;
+ // Deep compare each member.
+ if (!(result = has(b, key) && eq(a[key], b[key], aStack, bStack)))
+ break;
+ }
+ }
+ // Ensure that both objects contain the same number of properties.
+ if (result) {
+ for (key in b) {
+ if (has(b, key) && !(size--))
+ break;
+ }
+ result = !size;
+ }
+ }
+ // Remove the first object from the stack of traversed objects.
+ aStack.pop();
+ bStack.pop();
+ return result;
+ };
+
+ function describe(subjects) {
+ var descriptions = [];
+ Object.getOwnPropertyNames(subjects).forEach(function(name) {
+ if (name === "Description")
+ descriptions.push(subjects[name]);
+ else
+ descriptions.push(name + ": " + JSON.stringify(subjects[name]));
+ });
+ return descriptions.join(" ");
+ }
+
+ var predicates = {};
+
+ predicates.toBe = function(actual, expected) {
+ return {
+ "result": actual === expected,
+ "message": describe({
+ "Actual": actual,
+ "Expected": expected,
+ }),
+ };
+ };
+
+ predicates.toEqual = function(actual, expected) {
+ return {
+ "result": eq(actual, expected, [], []),
+ "message": describe({
+ "Actual": actual,
+ "Expected": expected,
+ }),
+ };
+ };
+
+ predicates.toBeDefined = function(actual) {
+ return {
+ "result": typeof actual !== "undefined",
+ "message": describe({
+ "Actual": actual,
+ "Description": "Expected a defined value",
+ }),
+ };
+ };
+
+ predicates.toBeUndefined = function(actual) {
+ // Recall: undefined is just a global variable. :)
+ return {
+ "result": typeof actual === "undefined",
+ "message": describe({
+ "Actual": actual,
+ "Description": "Expected an undefined value",
+ }),
+ };
+ };
+
+ predicates.toBeNull = function(actual) {
+ // Recall: typeof null === "object".
+ return {
+ "result": actual === null,
+ "message": describe({
+ "Actual": actual,
+ "Expected": null,
+ }),
+ };
+ };
+
+ predicates.toBeTruthy = function(actual) {
+ return {
+ "result": !!actual,
+ "message": describe({
+ "Actual": actual,
+ "Description": "Expected a truthy value",
+ }),
+ };
+ };
+
+ predicates.toBeFalsy = function(actual) {
+ return {
+ "result": !!!actual,
+ "message": describe({
+ "Actual": actual,
+ "Description": "Expected a falsy value",
+ }),
+ };
+ };
+
+ predicates.toContain = function(actual, element) {
+ return {
+ "result": (function () {
+ for (var i = 0; i < actual.length; ++i) {
+ if (eq(actual[i], element, [], []))
+ return true;
+ }
+ return false;
+ })(),
+ "message": describe({
+ "Actual": actual,
+ "Element": element,
+ }),
+ };
+ };
+
+ predicates.toBeLessThan = function(actual, reference) {
+ return {
+ "result": actual < reference,
+ "message": describe({
+ "Actual": actual,
+ "Reference": reference,
+ }),
+ };
+ };
+
+ predicates.toBeGreaterThan = function(actual, reference) {
+ return {
+ "result": actual > reference,
+ "message": describe({
+ "Actual": actual,
+ "Reference": reference,
+ }),
+ };
+ };
+
+ predicates.toThrow = function(actual) {
+ return {
+ "result": (function () {
+ if (!isFunction(actual))
+ throw new TypeError;
+ try {
+ actual();
+ } catch (ex) {
+ return true;
+ }
+ return false;
+ })(),
+ "message": "Expected function to throw",
+ };
+ }
+
+ function negate(predicate) {
+ return function() {
+ var outcome = predicate.apply(null, arguments);
+ outcome.result = !outcome.result;
+ return outcome;
+ }
+ }
+
+ function check(predicate) {
+ return function() {
+ var outcome = predicate.apply(null, arguments);
+ if (outcome.result)
+ return;
+ throw outcome.message;
+ };
+ }
+
+ function Condition(actual) {
+ this.not = {};
+ Object.getOwnPropertyNames(predicates).forEach(function(name) {
+ var bound = predicates[name].bind(null, actual);
+ this[name] = check(bound);
+ this.not[name] = check(negate(bound));
+ }, this);
+ }
+
+ return function(actual) {
+ return new Condition(actual);
+ };
+});
diff --git a/gin/test/file.cc b/gin/test/file.cc
new file mode 100644
index 0000000..0ed24e3
--- /dev/null
+++ b/gin/test/file.cc
@@ -0,0 +1,86 @@
+// 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/test/file.h"
+
+#include <iostream>
+
+#include "base/bind.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/path_service.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"
+
+using v8::ObjectTemplate;
+
+namespace gin {
+
+namespace {
+
+v8::Handle<v8::Value> ReadFileToString(gin::Arguments* args) {
+ std::string filename;
+ if (!args->GetNext(&filename))
+ return v8::Null(args->isolate());
+
+ const base::FilePath& path = base::FilePath::FromUTF8Unsafe(filename);
+ std::string contents;
+ if (!ReadFileToString(path, &contents))
+ return v8::Null(args->isolate());
+
+ return gin::Converter<std::string>::ToV8(args->isolate(), contents);
+}
+
+v8::Handle<v8::Value> GetSourceRootDirectory(gin::Arguments* args) {
+ base::FilePath path;
+ if (!PathService::Get(base::DIR_SOURCE_ROOT, &path))
+ return v8::Null(args->isolate());
+ return gin::Converter<std::string>::ToV8(args->isolate(),
+ path.AsUTF8Unsafe());
+}
+
+v8::Handle<v8::Value> GetFilesInDirectory(gin::Arguments* args) {
+ std::string filename;
+ if (!args->GetNext(&filename))
+ return v8::Null(args->isolate());
+
+ const base::FilePath& path = base::FilePath::FromUTF8Unsafe(filename);
+ if (!base::DirectoryExists(path))
+ return v8::Null(args->isolate());
+
+ std::vector<std::string> names;
+ base::FileEnumerator e(path, false, base::FileEnumerator::FILES);
+ for (base::FilePath name = e.Next(); !name.empty(); name = e.Next()) {
+ names.push_back(name.BaseName().AsUTF8Unsafe());
+ }
+
+ return gin::Converter<std::vector<std::string> >::ToV8(args->isolate(),
+ names);
+}
+
+gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin };
+
+} // namespace
+
+const char File::kModuleName[] = "file";
+
+v8::Local<v8::Value> File::GetModule(v8::Isolate* isolate) {
+ gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
+ v8::Local<ObjectTemplate> templ = data->GetObjectTemplate(&g_wrapper_info);
+ if (templ.IsEmpty()) {
+ templ = gin::ObjectTemplateBuilder(isolate)
+ .SetMethod("readFileToString", ReadFileToString)
+ .SetMethod("getFilesInDirectory", GetFilesInDirectory)
+ .SetMethod("getSourceRootDirectory", GetSourceRootDirectory)
+ .Build();
+ data->SetObjectTemplate(&g_wrapper_info, templ);
+ }
+ return templ->NewInstance();
+}
+
+} // namespace gin
diff --git a/gin/test/file.h b/gin/test/file.h
new file mode 100644
index 0000000..a4acd59
--- /dev/null
+++ b/gin/test/file.h
@@ -0,0 +1,21 @@
+// 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_TEST_FILE_H_
+#define GIN_TEST_FILE_H_
+
+#include "v8/include/v8.h"
+
+namespace gin {
+
+class File {
+ public:
+ static const char kModuleName[];
+ static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+};
+
+} // namespace gin
+
+#endif // GIN_TEST_FILE_H_
+
diff --git a/gin/test/file_runner.cc b/gin/test/file_runner.cc
new file mode 100644
index 0000000..83228d6
--- /dev/null
+++ b/gin/test/file_runner.cc
@@ -0,0 +1,82 @@
+// 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/test/file_runner.h"
+
+#include "base/files/file_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.h"
+#include "gin/array_buffer.h"
+#include "gin/converter.h"
+#include "gin/modules/console.h"
+#include "gin/modules/module_registry.h"
+#include "gin/public/context_holder.h"
+#include "gin/public/isolate_holder.h"
+#include "gin/test/file.h"
+#include "gin/test/gc.h"
+#include "gin/test/gtest.h"
+#include "gin/try_catch.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gin {
+
+namespace {
+
+std::vector<base::FilePath> GetModuleSearchPaths() {
+ std::vector<base::FilePath> search_paths(2);
+ PathService::Get(base::DIR_SOURCE_ROOT, &search_paths[0]);
+ PathService::Get(base::DIR_EXE, &search_paths[1]);
+ search_paths[1] = search_paths[1].AppendASCII("gen");
+ return search_paths;
+}
+
+} // namespace
+
+FileRunnerDelegate::FileRunnerDelegate()
+ : ModuleRunnerDelegate(GetModuleSearchPaths()) {
+ AddBuiltinModule(Console::kModuleName, Console::GetModule);
+ AddBuiltinModule(GTest::kModuleName, GTest::GetModule);
+ AddBuiltinModule(GC::kModuleName, GC::GetModule);
+ AddBuiltinModule(File::kModuleName, File::GetModule);
+}
+
+FileRunnerDelegate::~FileRunnerDelegate() {
+}
+
+void FileRunnerDelegate::UnhandledException(ShellRunner* runner,
+ TryCatch& try_catch) {
+ ModuleRunnerDelegate::UnhandledException(runner, try_catch);
+ FAIL() << try_catch.GetStackTrace();
+}
+
+void RunTestFromFile(const base::FilePath& path, FileRunnerDelegate* delegate,
+ bool run_until_idle) {
+ ASSERT_TRUE(base::PathExists(path)) << path.LossyDisplayName();
+ std::string source;
+ ASSERT_TRUE(ReadFileToString(path, &source));
+
+ base::MessageLoop message_loop;
+
+ gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode,
+ gin::ArrayBufferAllocator::SharedInstance());
+ gin::IsolateHolder instance;
+ gin::ShellRunner runner(delegate, instance.isolate());
+ {
+ gin::Runner::Scope scope(&runner);
+ v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
+ runner.Run(source, path.AsUTF8Unsafe());
+
+ if (run_until_idle) {
+ message_loop.RunUntilIdle();
+ } else {
+ message_loop.Run();
+ }
+
+ v8::Handle<v8::Value> result = runner.global()->Get(
+ StringToSymbol(runner.GetContextHolder()->isolate(), "result"));
+ EXPECT_EQ("PASS", V8ToString(result));
+ }
+}
+
+} // namespace gin
diff --git a/gin/test/file_runner.h b/gin/test/file_runner.h
new file mode 100644
index 0000000..f248fb5
--- /dev/null
+++ b/gin/test/file_runner.h
@@ -0,0 +1,38 @@
+// 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_TEST_FILE_RUNNER_H_
+#define GIN_TEST_FILE_RUNNER_H_
+
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "gin/modules/module_runner_delegate.h"
+#include "gin/runner.h"
+
+namespace gin {
+
+// FileRunnerDelegate is a simple RunnerDelegate that's useful for running
+// tests. The FileRunnerDelegate provides built-in modules for "console" and
+// "gtest" that are useful when writing unit tests.
+//
+// TODO(abarth): Rename FileRunnerDelegate to TestRunnerDelegate.
+class FileRunnerDelegate : public ModuleRunnerDelegate {
+ public:
+ FileRunnerDelegate();
+ virtual ~FileRunnerDelegate();
+
+ private:
+ // From ModuleRunnerDelegate:
+ virtual void UnhandledException(ShellRunner* runner,
+ TryCatch& try_catch) OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(FileRunnerDelegate);
+};
+
+void RunTestFromFile(const base::FilePath& path, FileRunnerDelegate* delegate,
+ bool run_until_idle = true);
+
+} // namespace gin
+
+#endif // GIN_TEST_FILE_RUNNER_H_
diff --git a/gin/test/file_unittests.js b/gin/test/file_unittests.js
new file mode 100644
index 0000000..8c25806
--- /dev/null
+++ b/gin/test/file_unittests.js
@@ -0,0 +1,37 @@
+// 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.
+
+define([
+ "gin/test/expect",
+ "file"
+ ], function(expect, file) {
+
+ function isString(x) {
+ return toString.call(x) === '[object String]'
+ }
+
+ var rootDir = file.getSourceRootDirectory();
+ expect(isString(rootDir)).toBeTruthy();
+
+ var noArgsNull = file.getFilesInDirectory();
+ expect(noArgsNull).toBeNull();
+
+ var files = file.getFilesInDirectory(rootDir);
+ expect(Array.isArray(files)).toBeTruthy();
+
+ var nsdNull = file.getFilesInDirectory(rootDir + "/no_such_dir");
+ expect(nsdNull).toBeNull();
+
+ var owners = file.readFileToString(rootDir + "/OWNERS");
+ expect(isString(owners)).toBeTruthy();
+ expect(owners.length).toBeGreaterThan(0);
+
+ noArgsNull = file.readFileToString();
+ expect(noArgsNull).toBeNull();
+
+ var nsfNull = file.readFileToString(rootDir + "/no_such_file");
+ expect(nsfNull).toBeNull();
+
+ this.result = "PASS";
+});
diff --git a/gin/test/gc.cc b/gin/test/gc.cc
new file mode 100644
index 0000000..4cd67e1
--- /dev/null
+++ b/gin/test/gc.cc
@@ -0,0 +1,41 @@
+// 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/test/gc.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "gin/arguments.h"
+#include "gin/converter.h"
+#include "gin/function_template.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "gin/public/wrapper_info.h"
+#include "gin/wrappable.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gin {
+
+namespace {
+WrapperInfo g_wrapper_info = { kEmbedderNativeGin };
+} // namespace
+
+const char GC::kModuleName[] = "gc";
+
+v8::Local<v8::Value> GC::GetModule(v8::Isolate* isolate) {
+ PerIsolateData* data = PerIsolateData::From(isolate);
+ v8::Local<v8::ObjectTemplate> templ =
+ data->GetObjectTemplate(&g_wrapper_info);
+ if (templ.IsEmpty()) {
+ templ = ObjectTemplateBuilder(isolate)
+ .SetMethod("collectGarbage",
+ base::Bind(&v8::Isolate::LowMemoryNotification,
+ base::Unretained(isolate)))
+ .Build();
+ data->SetObjectTemplate(&g_wrapper_info, templ);
+ }
+ return templ->NewInstance();
+}
+
+} // namespace gin
diff --git a/gin/test/gc.h b/gin/test/gc.h
new file mode 100644
index 0000000..25917ef
--- /dev/null
+++ b/gin/test/gc.h
@@ -0,0 +1,21 @@
+// 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_TEST_GC_H_
+#define GIN_TEST_GC_H_
+
+#include "v8/include/v8.h"
+
+namespace gin {
+
+// This module provides bindings to the garbage collector.
+class GC {
+ public:
+ static const char kModuleName[];
+ static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+};
+
+} // namespace gin
+
+#endif // GIN_TEST_GC_H_
diff --git a/gin/test/gtest.cc b/gin/test/gtest.cc
new file mode 100644
index 0000000..b4060a3
--- /dev/null
+++ b/gin/test/gtest.cc
@@ -0,0 +1,62 @@
+// 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/test/gtest.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "gin/arguments.h"
+#include "gin/converter.h"
+#include "gin/function_template.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "gin/public/wrapper_info.h"
+#include "gin/wrappable.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gin {
+
+namespace {
+
+void Fail(const std::string& description) {
+ FAIL() << description;
+}
+
+void ExpectTrue(bool condition, const std::string& description) {
+ EXPECT_TRUE(condition) << description;
+}
+
+void ExpectFalse(bool condition, const std::string& description) {
+ EXPECT_FALSE(condition) << description;
+}
+
+void ExpectEqual(const v8::Handle<v8::Value> expected,
+ const v8::Handle<v8::Value> actual,
+ const std::string& description) {
+ EXPECT_TRUE(expected->StrictEquals(actual)) << description;
+}
+
+WrapperInfo g_wrapper_info = { kEmbedderNativeGin };
+
+} // namespace
+
+const char GTest::kModuleName[] = "gtest";
+
+v8::Local<v8::Value> GTest::GetModule(v8::Isolate* isolate) {
+ PerIsolateData* data = PerIsolateData::From(isolate);
+ v8::Local<v8::ObjectTemplate> templ =
+ data->GetObjectTemplate(&g_wrapper_info);
+ if (templ.IsEmpty()) {
+ templ = ObjectTemplateBuilder(isolate)
+ .SetMethod("fail", Fail)
+ .SetMethod("expectTrue", ExpectTrue)
+ .SetMethod("expectFalse", ExpectFalse)
+ .SetMethod("expectEqual", ExpectEqual)
+ .Build();
+ data->SetObjectTemplate(&g_wrapper_info, templ);
+ }
+ return templ->NewInstance();
+}
+
+} // namespace gin
diff --git a/gin/test/gtest.h b/gin/test/gtest.h
new file mode 100644
index 0000000..8f4332d
--- /dev/null
+++ b/gin/test/gtest.h
@@ -0,0 +1,23 @@
+// 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_TEST_GTEST_H_
+#define GIN_TEST_GTEST_H_
+
+#include "v8/include/v8.h"
+
+namespace gin {
+
+// This module provides bindings to gtest. Most tests should use an idiomatic
+// JavaScript testing API, but this module is available for tests that need a
+// low-level integration with gtest.
+class GTest {
+ public:
+ static const char kModuleName[];
+ static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+};
+
+} // namespace gin
+
+#endif // GIN_TEST_GTEST_H_
diff --git a/gin/test/gtest_unittests.js b/gin/test/gtest_unittests.js
new file mode 100644
index 0000000..1d566d5
--- /dev/null
+++ b/gin/test/gtest_unittests.js
@@ -0,0 +1,11 @@
+// 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.
+
+define(["gtest"], function(gtest) {
+ gtest.expectTrue(true, "true is true");
+ gtest.expectFalse(false, "false is false");
+ gtest.expectTrue(this, "this is " + this);
+
+ this.result = "PASS";
+});
diff --git a/gin/test/run_all_unittests.cc b/gin/test/run_all_unittests.cc
new file mode 100644
index 0000000..25500a6
--- /dev/null
+++ b/gin/test/run_all_unittests.cc
@@ -0,0 +1,15 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+
+int main(int argc, char** argv) {
+ base::TestSuite test_suite(argc, argv);
+
+ return base::LaunchUnitTests(
+ argc, argv, base::Bind(&base::TestSuite::Run,
+ base::Unretained(&test_suite)));
+}
diff --git a/gin/test/run_js_tests.cc b/gin/test/run_js_tests.cc
new file mode 100644
index 0000000..b83dc9f
--- /dev/null
+++ b/gin/test/run_js_tests.cc
@@ -0,0 +1,43 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "gin/test/file_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gin {
+namespace {
+
+base::FilePath BasePath() {
+ base::FilePath path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ return path.AppendASCII("gin");
+}
+
+void RunTest(const base::FilePath& path) {
+ FileRunnerDelegate delegate;
+ RunTestFromFile(path, &delegate);
+}
+
+TEST(JSTest, File) {
+ RunTest(BasePath()
+ .AppendASCII("test")
+ .AppendASCII("file_unittests.js"));
+}
+
+TEST(JSTest, GTest) {
+ RunTest(BasePath()
+ .AppendASCII("test")
+ .AppendASCII("gtest_unittests.js"));
+}
+
+TEST(JSTest, ModuleRegistry) {
+ RunTest(BasePath()
+ .AppendASCII("modules")
+ .AppendASCII("module_registry_unittests.js"));
+}
+
+} // namespace
+} // gin
diff --git a/gin/test/v8_test.cc b/gin/test/v8_test.cc
new file mode 100644
index 0000000..cb6d573
--- /dev/null
+++ b/gin/test/v8_test.cc
@@ -0,0 +1,42 @@
+// 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/test/v8_test.h"
+
+#include "gin/array_buffer.h"
+#include "gin/public/isolate_holder.h"
+
+using v8::Context;
+using v8::Local;
+using v8::HandleScope;
+
+namespace gin {
+
+V8Test::V8Test() {
+}
+
+V8Test::~V8Test() {
+}
+
+void V8Test::SetUp() {
+ gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode,
+ gin::ArrayBufferAllocator::SharedInstance());
+ instance_.reset(new gin::IsolateHolder);
+ instance_->isolate()->Enter();
+ HandleScope handle_scope(instance_->isolate());
+ context_.Reset(instance_->isolate(), Context::New(instance_->isolate()));
+ Local<Context>::New(instance_->isolate(), context_)->Enter();
+}
+
+void V8Test::TearDown() {
+ {
+ HandleScope handle_scope(instance_->isolate());
+ Local<Context>::New(instance_->isolate(), context_)->Exit();
+ context_.Reset();
+ }
+ instance_->isolate()->Exit();
+ instance_.reset();
+}
+
+} // namespace gin
diff --git a/gin/test/v8_test.h b/gin/test/v8_test.h
new file mode 100644
index 0000000..e62aa57
--- /dev/null
+++ b/gin/test/v8_test.h
@@ -0,0 +1,38 @@
+// 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_TEST_V8_TEST_H_
+#define GIN_TEST_V8_TEST_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+class IsolateHolder;
+
+// V8Test is a simple harness for testing interactions with V8. V8Test doesn't
+// have any dependencies on Gin's module system.
+class V8Test : public testing::Test {
+ public:
+ V8Test();
+ virtual ~V8Test();
+
+ virtual void SetUp() OVERRIDE;
+ virtual void TearDown() OVERRIDE;
+
+ protected:
+ scoped_ptr<IsolateHolder> instance_;
+ v8::Persistent<v8::Context> context_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(V8Test);
+};
+
+} // namespace gin
+
+#endif // GIN_TEST_V8_TEST_H_
diff --git a/gin/try_catch.cc b/gin/try_catch.cc
new file mode 100644
index 0000000..a44e28e
--- /dev/null
+++ b/gin/try_catch.cc
@@ -0,0 +1,49 @@
+// 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/try_catch.h"
+
+#include <sstream>
+
+#include "gin/converter.h"
+
+namespace gin {
+
+TryCatch::TryCatch() {
+}
+
+TryCatch::~TryCatch() {
+}
+
+bool TryCatch::HasCaught() {
+ return try_catch_.HasCaught();
+}
+
+std::string TryCatch::GetStackTrace() {
+ if (!HasCaught()) {
+ return "";
+ }
+
+ std::stringstream ss;
+ v8::Handle<v8::Message> message = try_catch_.Message();
+ ss << V8ToString(message->Get()) << std::endl
+ << V8ToString(message->GetSourceLine()) << std::endl;
+
+ v8::Handle<v8::StackTrace> trace = message->GetStackTrace();
+ if (trace.IsEmpty())
+ return ss.str();
+
+ int len = trace->GetFrameCount();
+ for (int i = 0; i < len; ++i) {
+ v8::Handle<v8::StackFrame> frame = trace->GetFrame(i);
+ ss << V8ToString(frame->GetScriptName()) << ":"
+ << frame->GetLineNumber() << ":"
+ << frame->GetColumn() << ": "
+ << V8ToString(frame->GetFunctionName())
+ << std::endl;
+ }
+ return ss.str();
+}
+
+} // namespace gin
diff --git a/gin/try_catch.h b/gin/try_catch.h
new file mode 100644
index 0000000..633b909
--- /dev/null
+++ b/gin/try_catch.h
@@ -0,0 +1,33 @@
+// 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_TRY_CATCH_H_
+#define GIN_TRY_CATCH_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "gin/gin_export.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+// TryCatch is a convenient wrapper around v8::TryCatch.
+class GIN_EXPORT TryCatch {
+ public:
+ TryCatch();
+ ~TryCatch();
+
+ bool HasCaught();
+ std::string GetStackTrace();
+
+ private:
+ v8::TryCatch try_catch_;
+
+ DISALLOW_COPY_AND_ASSIGN(TryCatch);
+};
+
+} // namespace gin
+
+#endif // GIN_TRY_CATCH_H_
diff --git a/gin/v8_platform.cc b/gin/v8_platform.cc
new file mode 100644
index 0000000..d50ff24
--- /dev/null
+++ b/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/gin/wrappable.cc b/gin/wrappable.cc
new file mode 100644
index 0000000..a330fef
--- /dev/null
+++ b/gin/wrappable.cc
@@ -0,0 +1,87 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gin/wrappable.h"
+
+#include "base/logging.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+
+namespace gin {
+
+WrappableBase::WrappableBase() {
+}
+
+WrappableBase::~WrappableBase() {
+ wrapper_.Reset();
+}
+
+ObjectTemplateBuilder WrappableBase::GetObjectTemplateBuilder(
+ v8::Isolate* isolate) {
+ return ObjectTemplateBuilder(isolate);
+}
+
+void WrappableBase::WeakCallback(
+ const v8::WeakCallbackData<v8::Object, WrappableBase>& data) {
+ WrappableBase* wrappable = data.GetParameter();
+ wrappable->wrapper_.Reset();
+ delete wrappable;
+}
+
+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);
+ 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);
+ wrapper_.SetWeak(this, WeakCallback);
+ return wrapper;
+}
+
+namespace internal {
+
+void* FromV8Impl(v8::Isolate* isolate, v8::Handle<v8::Value> val,
+ WrapperInfo* wrapper_info) {
+ if (!val->IsObject())
+ return NULL;
+ v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(val);
+ WrapperInfo* info = WrapperInfo::From(obj);
+
+ // If this fails, the object is not managed by Gin. It is either a normal JS
+ // object that's not wrapping any external C++ object, or it is wrapping some
+ // C++ object, but that object isn't managed by Gin (maybe Blink).
+ if (!info)
+ return NULL;
+
+ // If this fails, the object is managed by Gin, but it's not wrapping an
+ // instance of the C++ class associated with wrapper_info.
+ if (info != wrapper_info)
+ return NULL;
+
+ return obj->GetAlignedPointerFromInternalField(kEncodedValueIndex);
+}
+
+} // namespace internal
+
+} // namespace gin
diff --git a/gin/wrappable.h b/gin/wrappable.h
new file mode 100644
index 0000000..ff52b19
--- /dev/null
+++ b/gin/wrappable.h
@@ -0,0 +1,116 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef GIN_WRAPPABLE_H_
+#define GIN_WRAPPABLE_H_
+
+#include "base/template_util.h"
+#include "gin/converter.h"
+#include "gin/gin_export.h"
+#include "gin/public/wrapper_info.h"
+
+namespace gin {
+
+namespace internal {
+
+GIN_EXPORT void* FromV8Impl(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val,
+ WrapperInfo* info);
+
+} // namespace internal
+
+
+// Wrappable is a base class for C++ objects that have corresponding v8 wrapper
+// objects. To retain a Wrappable object on the stack, use a gin::Handle.
+//
+// USAGE:
+// // my_class.h
+// class MyClass : Wrappable<MyClass> {
+// public:
+// static WrapperInfo kWrapperInfo;
+//
+// // Optional, only required if non-empty template should be used.
+// virtual gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
+// v8::Isolate* isolate);
+// ...
+// };
+//
+// // my_class.cc
+// 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
+// this static Create function will enforce that clients actually create a
+// wrapper for the object. If clients fail to create a wrapper for a wrappable
+// object, the object will leak because we use the weak callback from the
+// wrapper as the signal to delete the wrapped object.
+template<typename T>
+class Wrappable;
+
+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);
+
+ private:
+ static void WeakCallback(
+ const v8::WeakCallbackData<v8::Object, WrappableBase>& data);
+
+ v8::Persistent<v8::Object> wrapper_; // Weak
+
+ DISALLOW_COPY_AND_ASSIGN(WrappableBase);
+};
+
+
+template<typename T>
+class Wrappable : public WrappableBase {
+ public:
+ // Retrieve (or create) the v8 wrapper object cooresponding to this object.
+ // To customize the wrapper created for a subclass, override GetWrapperInfo()
+ // instead of overriding this function.
+ v8::Handle<v8::Object> GetWrapper(v8::Isolate* isolate) {
+ return GetWrapperImpl(isolate, &T::kWrapperInfo);
+ }
+
+ protected:
+ Wrappable() {}
+ virtual ~Wrappable() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Wrappable);
+};
+
+
+// This converter handles any subclass of Wrappable.
+template<typename T>
+struct Converter<T*, typename base::enable_if<
+ 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*>(static_cast<WrappableBase*>(
+ internal::FromV8Impl(isolate, val, &T::kWrapperInfo)));
+ return *out != NULL;
+ }
+};
+
+} // namespace gin
+
+#endif // GIN_WRAPPABLE_H_
diff --git a/gin/wrappable_unittest.cc b/gin/wrappable_unittest.cc
new file mode 100644
index 0000000..0e10c32
--- /dev/null
+++ b/gin/wrappable_unittest.cc
@@ -0,0 +1,273 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "gin/arguments.h"
+#include "gin/handle.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 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;
+
+ static gin::Handle<MyObject> Create(v8::Isolate* isolate) {
+ return CreateHandle(isolate, new MyObject());
+ }
+
+ int value() const { return value_; }
+ void set_value(int value) { value_ = value; }
+
+ 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, const gin::Arguments& arguments) {
+ if (arguments.IsConstructCall())
+ arguments.ThrowTypeError("Cannot be called as constructor.");
+ else
+ result_ = val;
+ }
+
+ int result_;
+};
+
+class MyObject2 : public Wrappable<MyObject2> {
+ public:
+ static WrapperInfo kWrapperInfo;
+};
+
+class MyObjectBlink : public Wrappable<MyObjectBlink> {
+ public:
+ static WrapperInfo kWrapperInfo;
+};
+
+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 };
+
+typedef V8Test WrappableTest;
+
+TEST_F(WrappableTest, WrapAndUnwrap) {
+ v8::Isolate* isolate = instance_->isolate();
+ v8::HandleScope handle_scope(isolate);
+
+ Handle<MyObject> obj = MyObject::Create(isolate);
+
+ v8::Handle<v8::Value> wrapper = ConvertToV8(isolate, obj.get());
+ EXPECT_FALSE(wrapper.IsEmpty());
+
+ MyObject* unwrapped = NULL;
+ EXPECT_TRUE(ConvertFromV8(isolate, wrapper, &unwrapped));
+ EXPECT_EQ(obj.get(), unwrapped);
+}
+
+TEST_F(WrappableTest, UnwrapFailures) {
+ v8::Isolate* isolate = instance_->isolate();
+ v8::HandleScope handle_scope(isolate);
+
+ // Something that isn't an object.
+ v8::Handle<v8::Value> thing = v8::Number::New(isolate, 42);
+ MyObject* unwrapped = NULL;
+ EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped));
+ EXPECT_FALSE(unwrapped);
+
+ // An object that's not wrapping anything.
+ thing = v8::Object::New(isolate);
+ EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped));
+ EXPECT_FALSE(unwrapped);
+
+ // An object that's wrapping a C++ object from Blink.
+ thing.Clear();
+ thing = ConvertToV8(isolate, new MyObjectBlink());
+ EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped));
+ EXPECT_FALSE(unwrapped);
+
+ // An object that's wrapping a C++ object of the wrong type.
+ thing.Clear();
+ thing = ConvertToV8(isolate, new MyObject2());
+ EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped));
+ EXPECT_FALSE(unwrapped);
+}
+
+TEST_F(WrappableTest, GetAndSetProperty) {
+ v8::Isolate* isolate = instance_->isolate();
+ v8::HandleScope handle_scope(isolate);
+
+ gin::Handle<MyObject> obj = MyObject::Create(isolate);
+
+ obj->set_value(42);
+ EXPECT_EQ(42, obj->value());
+
+ v8::Handle<v8::String> source = StringToV8(isolate,
+ "(function (obj) {"
+ " if (obj.value !== 42) throw 'FAIL';"
+ " else obj.value = 191; })");
+ EXPECT_FALSE(source.IsEmpty());
+
+ 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(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, 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());
+}
+
+TEST_F(WrappableTest, CallAsConstructor) {
+ 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) {"
+ "new 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_TRUE(try_catch.HasCaught());
+}
+
+} // namespace gin
diff --git a/gin/wrapper_info.cc b/gin/wrapper_info.cc
new file mode 100644
index 0000000..6bf8316
--- /dev/null
+++ b/gin/wrapper_info.cc
@@ -0,0 +1,17 @@
+// 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/public/wrapper_info.h"
+
+namespace gin {
+
+WrapperInfo* WrapperInfo::From(v8::Handle<v8::Object> object) {
+ if (object->InternalFieldCount() != kNumberOfInternalFields)
+ return NULL;
+ WrapperInfo* info = static_cast<WrapperInfo*>(
+ object->GetAlignedPointerFromInternalField(kWrapperInfoIndex));
+ return info->embedder == kEmbedderNativeGin ? info : NULL;
+}
+
+} // namespace gin