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