diff options
author | Andras Becsi <andras.becsi@digia.com> | 2014-03-18 13:16:26 +0100 |
---|---|---|
committer | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2014-03-20 15:55:39 +0100 |
commit | 3f0f86b0caed75241fa71c95a5d73bc0164348c5 (patch) | |
tree | 92b9fb00f2e9e90b0be2262093876d4f43b6cd13 /chromium/gin | |
parent | e90d7c4b152c56919d963987e2503f9909a666d2 (diff) | |
download | qtwebengine-chromium-3f0f86b0caed75241fa71c95a5d73bc0164348c5.tar.gz |
Update to new stable branch 1750
This also includes an updated ninja and chromium dependencies
needed on Windows.
Change-Id: Icd597d80ed3fa4425933c9f1334c3c2e31291c42
Reviewed-by: Zoltan Arvai <zarvai@inf.u-szeged.hu>
Reviewed-by: Zeno Albisser <zeno.albisser@digia.com>
Diffstat (limited to 'chromium/gin')
49 files changed, 3911 insertions, 0 deletions
diff --git a/chromium/gin/DEPS b/chromium/gin/DEPS new file mode 100644 index 00000000000..4e3f30adc78 --- /dev/null +++ b/chromium/gin/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+base", + "+v8", +] diff --git a/chromium/gin/OWNERS b/chromium/gin/OWNERS new file mode 100644 index 00000000000..35dde41b938 --- /dev/null +++ b/chromium/gin/OWNERS @@ -0,0 +1,3 @@ +aa@chromium.org +abarth@chromium.org +jochen@chromium.org diff --git a/chromium/gin/README b/chromium/gin/README new file mode 100644 index 00000000000..82d06188f3f --- /dev/null +++ b/chromium/gin/README @@ -0,0 +1,8 @@ +Gin - Lightweight bindings for V8 +================================= + +This directory contains gin, a lightweight bindings library for V8. These +bindings are not compatible with the V8 bindings used by Blink because both +want to control the v8::Isolate's internal data field. Maybe in some future +world we'll refactor the Blink V8 bindings to use this system. In the meantime, +these bindings are convenient for projects other than Blink that use V8. diff --git a/chromium/gin/arguments.cc b/chromium/gin/arguments.cc new file mode 100644 index 00000000000..c43ff283aa2 --- /dev/null +++ b/chromium/gin/arguments.cc @@ -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. + +#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))); +} + +} // namespace gin diff --git a/chromium/gin/arguments.h b/chromium/gin/arguments.h new file mode 100644 index 00000000000..ec4ae80e882 --- /dev/null +++ b/chromium/gin/arguments.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_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; + } + + 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_; } + + private: + v8::Isolate* isolate_; + const v8::FunctionCallbackInfo<v8::Value>* info_; + int next_; + bool insufficient_arguments_; +}; + +} // namespace gin + +#endif // GIN_ARGUMENTS_H_ diff --git a/chromium/gin/array_buffer.cc b/chromium/gin/array_buffer.cc new file mode 100644 index 00000000000..ee9f2a5867b --- /dev/null +++ b/chromium/gin/array_buffer.cc @@ -0,0 +1,176 @@ +// 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/array_buffer.h" + +#include <stdlib.h> + +namespace gin { + +COMPILE_ASSERT(V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT == 2, + array_buffers_must_have_two_internal_fields); + +static const int kBufferViewPrivateIndex = 0; + +// ArrayBufferAllocator ------------------------------------------------------- + +void* ArrayBufferAllocator::Allocate(size_t length) { + 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_; + void* buffer_; + size_t length_; +}; + +scoped_refptr<ArrayBuffer::Private> ArrayBuffer::Private::From( + v8::Isolate* isolate, v8::Handle<v8::ArrayBuffer> array) { + if (array->IsExternal()) { + return make_scoped_refptr(static_cast<Private*>( + array->GetAlignedPointerFromInternalField(kBufferViewPrivateIndex))); + } + return make_scoped_refptr(new Private(isolate, array)); +} + +ArrayBuffer::Private::Private(v8::Isolate* isolate, + v8::Handle<v8::ArrayBuffer> array) + : array_buffer_(isolate, array) { + // Take ownership of the array buffer. + v8::ArrayBuffer::Contents contents = array->Externalize(); + buffer_ = contents.Data(); + length_ = contents.ByteLength(); + + array->SetAlignedPointerInInternalField(kBufferViewPrivateIndex, this); + + self_reference_ = this; // Cleared in WeakCallback. + array_buffer_.SetWeak(this, WeakCallback); +} + +ArrayBuffer::Private::~Private() { + ArrayBufferAllocator::SharedInstance()->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() { +} + +// 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/chromium/gin/array_buffer.h b/chromium/gin/array_buffer.h new file mode 100644 index 00000000000..7886fa9be05 --- /dev/null +++ b/chromium/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; + + static ArrayBufferAllocator* SharedInstance(); +}; + +class GIN_EXPORT ArrayBuffer { + public: + ArrayBuffer(); + explicit ArrayBuffer(v8::Isolate* isolate); + ArrayBuffer(v8::Isolate* isolate, v8::Handle<v8::ArrayBuffer> buffer); + ~ArrayBuffer(); + ArrayBuffer& operator=(const ArrayBuffer& other); + + 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(); + + 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/chromium/gin/context_holder.cc b/chromium/gin/context_holder.cc new file mode 100644 index 00000000000..32b50518376 --- /dev/null +++ b/chromium/gin/context_holder.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/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() { + v8::HandleScope handle_scope(isolate()); + v8::Handle<v8::Context> context = this->context(); + + data_->Detach(context); + data_.reset(); + + // TODO(abarth): Figure out how to set kResetInDestructor to true. + context_.Reset(); +} + +void ContextHolder::SetContext(v8::Handle<v8::Context> context) { + DCHECK(context_.IsEmpty()); + context_.Reset(isolate_, context); + data_.reset(new PerContextData(context)); +} + +} // namespace gin diff --git a/chromium/gin/converter.cc b/chromium/gin/converter.cc new file mode 100644 index 00000000000..54736f95aaf --- /dev/null +++ b/chromium/gin/converter.cc @@ -0,0 +1,193 @@ +// 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<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/chromium/gin/converter.h b/chromium/gin/converter.h new file mode 100644 index 00000000000..ee029a7f770 --- /dev/null +++ b/chromium/gin/converter.h @@ -0,0 +1,194 @@ +// 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<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/chromium/gin/converter_unittest.cc b/chromium/gin/converter_unittest.cc new file mode 100644 index 00000000000..9b831b91ac0 --- /dev/null +++ b/chromium/gin/converter_unittest.cc @@ -0,0 +1,133 @@ +// 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(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/chromium/gin/dictionary.cc b/chromium/gin/dictionary.cc new file mode 100644 index 00000000000..d3361228bb2 --- /dev/null +++ b/chromium/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/chromium/gin/dictionary.h b/chromium/gin/dictionary.h new file mode 100644 index 00000000000..972108fbb43 --- /dev/null +++ b/chromium/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/chromium/gin/function_template.cc b/chromium/gin/function_template.cc new file mode 100644 index 00000000000..5dd5ecd3d50 --- /dev/null +++ b/chromium/gin/function_template.cc @@ -0,0 +1,26 @@ +// 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" + +#include "gin/per_isolate_data.h" + +namespace gin { + +WrapperInfo internal::CallbackHolderBase::kWrapperInfo = { kEmbedderNativeGin }; + +void InitFunctionTemplates(PerIsolateData* isolate_data) { + if (!isolate_data->GetObjectTemplate( + &internal::CallbackHolderBase::kWrapperInfo).IsEmpty()) { + return; + } + + v8::Handle<v8::ObjectTemplate> templ( + v8::ObjectTemplate::New(isolate_data->isolate())); + templ->SetInternalFieldCount(kNumberOfInternalFields); + isolate_data->SetObjectTemplate(&internal::CallbackHolderBase::kWrapperInfo, + templ); +} + +} // namespace gin diff --git a/chromium/gin/function_template.h b/chromium/gin/function_template.h new file mode 100644 index 00000000000..14f6e78fe2f --- /dev/null +++ b/chromium/gin/function_template.h @@ -0,0 +1,357 @@ +// 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 "gin/handle.h" +#include "gin/public/gin_embedders.h" +#include "gin/public/wrapper_info.h" +#include "gin/wrappable.h" + +#include "v8/include/v8.h" + +namespace gin { + +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. +// +// v8::FunctionTemplate only supports passing void* as data so how do we know +// when to delete the base::Callback? That's where CallbackHolderBase comes in. +// It inherits from Wrappable, which delete itself when both (a) the refcount +// via base::RefCounted has dropped to zero, and (b) there are no more +// JavaScript references in V8. + +// This simple base class is used so that we can share a single object template +// among every CallbackHolder instance. +class GIN_EXPORT CallbackHolderBase : public Wrappable<CallbackHolderBase> { + public: + static WrapperInfo kWrapperInfo; + protected: + virtual ~CallbackHolderBase() {} +}; + +template<typename Sig> +class CallbackHolder : public CallbackHolderBase { + public: + CallbackHolder(const base::Callback<Sig>& callback, int flags) + : callback(callback), flags(flags) {} + base::Callback<Sig> callback; + int flags; + private: + virtual ~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> +struct Invoker { + 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> { + 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> { + 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> { + 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> { + 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> { + 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> { + 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> { + 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> { + inline static void Go( + Arguments* args, + const base::Callback<R()>& callback) { + args->Return(callback.Run()); + } +}; +template<> +struct Invoker<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; +} + + +// 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); + CallbackHolderBase* holder_base = NULL; + CHECK(args.GetData(&holder_base)); + + 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); + CallbackHolderBase* holder_base = NULL; + CHECK(args.GetData(&holder_base)); + + 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); + CallbackHolderBase* holder_base = NULL; + CHECK(args.GetData(&holder_base)); + + 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); + CallbackHolderBase* holder_base = NULL; + CHECK(args.GetData(&holder_base)); + + 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); + CallbackHolderBase* holder_base = NULL; + CHECK(args.GetData(&holder_base)); + + 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); + } +}; + +} // namespace internal + + +// This should be called once per-isolate to initialize the function template +// system. +GIN_EXPORT void InitFunctionTemplates(PerIsolateData* isolate_data); + + +// CreateFunctionTemplate creates a v8::FunctionTemplate that will create +// JavaScript functions that execute a provided C++ function or base::Callback. +// JavaScript arguments are automatically converted via gin::Converter, as is +// 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; + gin::Handle<HolderT> holder = CreateHandle( + isolate, new HolderT(callback, callback_flags)); + return v8::FunctionTemplate::New( + &internal::Dispatcher<Sig>::DispatchToCallback, + ConvertToV8<internal::CallbackHolderBase*>(isolate, holder.get())); +} + +} // namespace gin + +#endif // GIN_FUNCTION_TEMPLATE_H_ diff --git a/chromium/gin/function_template.h.pump b/chromium/gin/function_template.h.pump new file mode 100644 index 00000000000..fad718febc2 --- /dev/null +++ b/chromium/gin/function_template.h.pump @@ -0,0 +1,213 @@ +$$ 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 = 4 + +// 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 "gin/handle.h" +#include "gin/public/gin_embedders.h" +#include "gin/public/wrapper_info.h" +#include "gin/wrappable.h" + +#include "v8/include/v8.h" + +namespace gin { + +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. +// +// v8::FunctionTemplate only supports passing void* as data so how do we know +// when to delete the base::Callback? That's where CallbackHolderBase comes in. +// It inherits from Wrappable, which delete itself when both (a) the refcount +// via base::RefCounted has dropped to zero, and (b) there are no more +// JavaScript references in V8. + +// This simple base class is used so that we can share a single object template +// among every CallbackHolder instance. +class GIN_EXPORT CallbackHolderBase : public Wrappable<CallbackHolderBase> { + public: + static WrapperInfo kWrapperInfo; + + protected: + virtual ~CallbackHolderBase() {} +}; + +template<typename Sig> +class CallbackHolder : public CallbackHolderBase { + public: + CallbackHolder(const base::Callback<Sig>& callback, int flags) + : callback(callback), flags(flags) {} + base::Callback<Sig> callback; + int flags; + private: + virtual ~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; +} + + +// 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); + CallbackHolderBase* holder_base = NULL; + CHECK(args.GetData(&holder_base)); + + 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 + + +// This should be called once per-isolate to initialize the function template +// system. +GIN_EXPORT void InitFunctionTemplates(PerIsolateData* isolate_data); + + +// CreateFunctionTemplate creates a v8::FunctionTemplate that will create +// JavaScript functions that execute a provided C++ function or base::Callback. +// JavaScript arguments are automatically converted via gin::Converter, as is +// 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; + gin::Handle<HolderT> holder = CreateHandle( + isolate, new HolderT(callback, callback_flags)); + return v8::FunctionTemplate::New( + &internal::Dispatcher<Sig>::DispatchToCallback, + ConvertToV8<internal::CallbackHolderBase*>(isolate, holder.get())); +} + +} // namespace gin + +#endif // GIN_FUNCTION_TEMPLATE_H_ diff --git a/chromium/gin/gin.gyp b/chromium/gin/gin.gyp new file mode 100644 index 00000000000..8af1e8b85e0 --- /dev/null +++ b/chromium/gin/gin.gyp @@ -0,0 +1,122 @@ +# 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', + '../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', + 'dictionary.cc', + 'dictionary.h', + 'function_template.cc', + 'function_template.h', + 'gin_export.h', + 'handle.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_runner_delegate.cc', + 'modules/module_runner_delegate.h', + 'object_template_builder.cc', + 'object_template_builder.h', + 'per_context_data.cc', + 'per_context_data.h', + 'per_isolate_data.cc', + 'per_isolate_data.h', + 'public/context_holder.h', + 'public/gin_embedders.h', + 'public/isolate_holder.h', + 'public/wrapper_info.h', + 'runner.cc', + 'runner.h', + 'try_catch.cc', + 'try_catch.h', + '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_runner.cc', + 'test/file_runner.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:run_all_unittests', + '../v8/tools/gyp/v8.gyp:v8', + 'gin_test', + ], + 'sources': [ + 'converter_unittest.cc', + 'test/run_all_unittests.cc', + 'test/run_js_tests.cc', + 'runner_unittest.cc', + 'wrappable_unittest.cc', + ], + }, + ], +} diff --git a/chromium/gin/gin_export.h b/chromium/gin/gin_export.h new file mode 100644 index 00000000000..fc4bf9bc003 --- /dev/null +++ b/chromium/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/chromium/gin/handle.h b/chromium/gin/handle.h new file mode 100644 index 00000000000..da1de347f23 --- /dev/null +++ b/chromium/gin/handle.h @@ -0,0 +1,68 @@ +// 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) { + return gin::Handle<T>(object->GetWrapper(isolate), object); +} + +} // namespace gin + +#endif // GIN_HANDLE_H_ diff --git a/chromium/gin/isolate_holder.cc b/chromium/gin/isolate_holder.cc new file mode 100644 index 00000000000..1929ebd0342 --- /dev/null +++ b/chromium/gin/isolate_holder.cc @@ -0,0 +1,79 @@ +// 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/rand_util.h" +#include "base/sys_info.h" +#include "gin/array_buffer.h" +#include "gin/function_template.h" +#include "gin/per_isolate_data.h" + +namespace gin { + +namespace { + +bool GenerateEntropy(unsigned char* buffer, size_t amount) { + base::RandBytes(buffer, amount); + return true; +} + + +void EnsureV8Initialized(bool gin_managed) { + static bool v8_is_initialized = false; + static bool v8_is_gin_managed = false; + if (v8_is_initialized) { + CHECK_EQ(v8_is_gin_managed, gin_managed); + return; + } + v8_is_initialized = true; + v8_is_gin_managed = gin_managed; + if (!gin_managed) + return; + + v8::V8::SetArrayBufferAllocator(ArrayBufferAllocator::SharedInstance()); + static const char v8_flags[] = "--use_strict --harmony"; + v8::V8::SetFlagsFromString(v8_flags, sizeof(v8_flags) - 1); + v8::V8::SetEntropySource(&GenerateEntropy); + v8::V8::Initialize(); +} + +} // namespace + +IsolateHolder::IsolateHolder() + : isolate_owner_(true) { + EnsureV8Initialized(true); + isolate_ = v8::Isolate::New(); + v8::ResourceConstraints constraints; + constraints.ConfigureDefaults(base::SysInfo::AmountOfPhysicalMemory(), + base::SysInfo::NumberOfProcessors()); + v8::SetResourceConstraints(isolate_, &constraints); + Init(); +} + +IsolateHolder::IsolateHolder(v8::Isolate* isolate) + : isolate_owner_(false), + isolate_(isolate) { + EnsureV8Initialized(false); + Init(); +} + +IsolateHolder::~IsolateHolder() { + isolate_data_.reset(); + if (isolate_owner_) + isolate_->Dispose(); +} + +void IsolateHolder::Init() { + v8::Isolate::Scope isolate_scope(isolate_); + v8::HandleScope handle_scope(isolate_); + isolate_data_.reset(new PerIsolateData(isolate_)); + InitFunctionTemplates(isolate_data_.get()); +} + +} // namespace gin diff --git a/chromium/gin/modules/console.cc b/chromium/gin/modules/console.cc new file mode 100644 index 00000000000..14849944bfa --- /dev/null +++ b/chromium/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/per_isolate_data.h" +#include "gin/public/wrapper_info.h" + +using v8::ObjectTemplate; + +namespace gin { + +namespace { + +void Log(const v8::FunctionCallbackInfo<v8::Value>& info) { + Arguments args(info); + + std::vector<std::string> messages; + if (!args.GetRemaining(&messages)) + return args.ThrowTypeError("Expected strings."); + + std::cout << JoinString(messages, ' ') << std::endl; +} + +WrapperInfo g_wrapper_info = { kEmbedderNativeGin }; + +} // namespace + +const char Console::kModuleName[] = "console"; + +v8::Local<ObjectTemplate> Console::GetTemplate(v8::Isolate* isolate) { + PerIsolateData* data = PerIsolateData::From(isolate); + v8::Local<ObjectTemplate> templ = data->GetObjectTemplate(&g_wrapper_info); + if (templ.IsEmpty()) { + templ = ObjectTemplate::New(); + templ->Set(StringToSymbol(isolate, "log"), + v8::FunctionTemplate::New(isolate, Log)); + data->SetObjectTemplate(&g_wrapper_info, templ); + } + return templ; +} + +} // namespace gin diff --git a/chromium/gin/modules/console.h b/chromium/gin/modules/console.h new file mode 100644 index 00000000000..8753961dd2b --- /dev/null +++ b/chromium/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::ObjectTemplate> GetTemplate(v8::Isolate* isolate); +}; + +} // namespace gin + +#endif // GIN_MODULES_CONSOLE_H_ diff --git a/chromium/gin/modules/file_module_provider.cc b/chromium/gin/modules/file_module_provider.cc new file mode 100644 index 00000000000..990dcd000c0 --- /dev/null +++ b/chromium/gin/modules/file_module_provider.cc @@ -0,0 +1,72 @@ +// 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/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()); + v8::Handle<v8::Script> script = v8::Script::New( + StringToV8(runner->isolate(), source), + StringToV8(runner->isolate(), id)); + runner->Run(script); + return; + } +} + +} // 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/chromium/gin/modules/file_module_provider.h b/chromium/gin/modules/file_module_provider.h new file mode 100644 index 00000000000..dd75a0feaf1 --- /dev/null +++ b/chromium/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/chromium/gin/modules/module_registry.cc b/chromium/gin/modules/module_registry.cc new file mode 100644 index 00000000000..9dd3dbe9810 --- /dev/null +++ b/chromium/gin/modules/module_registry.cc @@ -0,0 +1,248 @@ +// 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/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 { + +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; +} + +v8::Handle<String> GetHiddenValueKey(Isolate* isolate) { + return StringToSymbol(isolate, "::gin::ModuleRegistry"); +} + +} // namespace + +ModuleRegistry::ModuleRegistry(Isolate* isolate) + : modules_(isolate, Object::New()) { +} + +ModuleRegistry::~ModuleRegistry() { + modules_.Reset(); +} + +void ModuleRegistry::RegisterGlobals(Isolate* isolate, + v8::Handle<ObjectTemplate> templ) { + templ->Set(StringToSymbol(isolate, "define"), GetDefineTemplate(isolate)); +} + +ModuleRegistry* ModuleRegistry::From(v8::Handle<Context> context) { + Isolate* isolate = context->GetIsolate(); + v8::Handle<String> key = GetHiddenValueKey(isolate); + v8::Handle<Value> value = context->Global()->GetHiddenValue(key); + v8::Handle<External> external; + if (value.IsEmpty() || !ConvertFromV8(isolate, value, &external)) { + PerContextData* data = PerContextData::From(context); + if (!data) + return NULL; + ModuleRegistry* registry = new ModuleRegistry(isolate); + context->Global()->SetHiddenValue(key, External::New(isolate, registry)); + data->AddSupplement(scoped_ptr<ContextSupplement>(registry)); + return registry; + } + return static_cast<ModuleRegistry*>(external->Value()); +} + +void ModuleRegistry::AddBuiltinModule(Isolate* isolate, + const std::string& id, + v8::Handle<ObjectTemplate> templ) { + DCHECK(!id.empty()); + RegisterModule(isolate, id, templ->NewInstance()); +} + +void ModuleRegistry::AddPendingModule(Isolate* isolate, + scoped_ptr<PendingModule> pending) { + AttemptToLoad(isolate, pending.Pass()); +} + +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; + } + // Should we support multiple callers waiting on the same module? + DCHECK(waiting_callbacks_.find(id) == waiting_callbacks_.end()); + waiting_callbacks_[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); + + LoadModuleCallbackMap::iterator it = waiting_callbacks_.find(id); + if (it == waiting_callbacks_.end()) + return; + LoadModuleCallback callback = it->second; + waiting_callbacks_.erase(it); + // Should we call the callback asynchronously? + callback.Run(module); +} + +void ModuleRegistry::Detach(v8::Handle<Context> context) { + context->Global()->SetHiddenValue(GetHiddenValueKey(context->GetIsolate()), + v8::Handle<Value>()); +} + +bool ModuleRegistry::CheckDependencies(PendingModule* pending) { + size_t num_missing_dependencies = 0; + size_t len = pending->dependencies.size(); + 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/chromium/gin/modules/module_registry.h b/chromium/gin/modules/module_registry.h new file mode 100644 index 00000000000..27606cd9a54 --- /dev/null +++ b/chromium/gin/modules/module_registry.h @@ -0,0 +1,101 @@ +// 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 "gin/gin_export.h" +#include "gin/per_context_data.h" + +namespace gin { + +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 ContextSupplement { + 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); + + // The caller must have already entered our context. + void AddBuiltinModule(v8::Isolate* isolate, + const std::string& id, + v8::Handle<v8::ObjectTemplate> templ); + + // 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::map<std::string, LoadModuleCallback> LoadModuleCallbackMap; + + explicit ModuleRegistry(v8::Isolate* isolate); + + // From ContextSupplement: + virtual void Detach(v8::Handle<v8::Context> context) OVERRIDE; + + void Load(v8::Isolate* isolate, scoped_ptr<PendingModule> pending); + void RegisterModule(v8::Isolate* isolate, + const std::string& id, + 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_; + + DISALLOW_COPY_AND_ASSIGN(ModuleRegistry); +}; + +} // namespace gin + +#endif // GIN_MODULES_MODULE_REGISTRY_H_ diff --git a/chromium/gin/modules/module_registry_unittests.js b/chromium/gin/modules/module_registry_unittests.js new file mode 100644 index 00000000000..ca7014899b3 --- /dev/null +++ b/chromium/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/chromium/gin/modules/module_runner_delegate.cc b/chromium/gin/modules/module_runner_delegate.cc new file mode 100644 index 00000000000..9bf2863c203 --- /dev/null +++ b/chromium/gin/modules/module_runner_delegate.cc @@ -0,0 +1,55 @@ +// 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 "gin/modules/module_registry.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, + ModuleTemplateGetter templ) { + builtin_modules_[id] = templ; +} + +void ModuleRunnerDelegate::AttemptToLoadMoreModules(Runner* runner) { + ModuleRegistry* registry = ModuleRegistry::From(runner->context()); + registry->AttemptToLoadMoreModules(runner->isolate()); + module_provider_.AttempToLoadModules( + runner, registry->unsatisfied_dependencies()); +} + +v8::Handle<v8::ObjectTemplate> ModuleRunnerDelegate::GetGlobalTemplate( + Runner* runner) { + v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); + ModuleRegistry::RegisterGlobals(runner->isolate(), templ); + return templ; +} + +void ModuleRunnerDelegate::DidCreateContext(Runner* runner) { + RunnerDelegate::DidCreateContext(runner); + + v8::Handle<v8::Context> context = runner->context(); + ModuleRegistry* registry = ModuleRegistry::From(context); + + for (BuiltinModuleMap::const_iterator it = builtin_modules_.begin(); + it != builtin_modules_.end(); ++it) { + registry->AddBuiltinModule(runner->isolate(), it->first, + it->second(runner->isolate())); + } +} + +void ModuleRunnerDelegate::DidRunScript(Runner* runner) { + AttemptToLoadMoreModules(runner); +} + +} // namespace gin diff --git a/chromium/gin/modules/module_runner_delegate.h b/chromium/gin/modules/module_runner_delegate.h new file mode 100644 index 00000000000..06077f479bb --- /dev/null +++ b/chromium/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/compiler_specific.h" +#include "gin/gin_export.h" +#include "gin/modules/file_module_provider.h" +#include "gin/runner.h" + +namespace gin { + +typedef v8::Local<v8::ObjectTemplate> (*ModuleTemplateGetter)( + v8::Isolate* isolate); + +// Emebedders that use AMD modules will probably want to use a RunnerDelegate +// that inherits from ModuleRunnerDelegate. ModuleRunnerDelegate lets embedders +// register built-in modules and routes module requests to FileModuleProvider. +class GIN_EXPORT ModuleRunnerDelegate : public RunnerDelegate { + public: + explicit ModuleRunnerDelegate( + const std::vector<base::FilePath>& search_paths); + virtual ~ModuleRunnerDelegate(); + + // Lets you register a built-in module. Built-in modules are instantiated by + // creating a new instance of a v8::ObjectTemplate rather than by executing + // code. This function takes a ModuleTemplateGetter rather than a + // v8::ObjectTemplate directly so that embedders can create object templates + // lazily. + void AddBuiltinModule(const std::string& id, ModuleTemplateGetter templ); + + protected: + void AttemptToLoadMoreModules(Runner* runner); + + private: + typedef std::map<std::string, ModuleTemplateGetter> BuiltinModuleMap; + + // From RunnerDelegate: + virtual v8::Handle<v8::ObjectTemplate> GetGlobalTemplate( + Runner* runner) OVERRIDE; + virtual void DidCreateContext(Runner* runner) OVERRIDE; + virtual void DidRunScript(Runner* 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/chromium/gin/object_template_builder.cc b/chromium/gin/object_template_builder.cc new file mode 100644 index 00000000000..6dc6d2fe849 --- /dev/null +++ b/chromium/gin/object_template_builder.cc @@ -0,0 +1,36 @@ +// 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" + +namespace gin { + +ObjectTemplateBuilder::ObjectTemplateBuilder(v8::Isolate* isolate) + : isolate_(isolate), template_(v8::ObjectTemplate::New(isolate)) { +} + +ObjectTemplateBuilder::~ObjectTemplateBuilder() { +} + +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/chromium/gin/object_template_builder.h b/chromium/gin/object_template_builder.h new file mode 100644 index 00000000000..6367b71245c --- /dev/null +++ b/chromium/gin/object_template_builder.h @@ -0,0 +1,124 @@ +// 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)); + } +}; + +// 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); + } +}; + +// 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); + } +}; + +// 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)); + } + + 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/chromium/gin/per_context_data.cc b/chromium/gin/per_context_data.cc new file mode 100644 index 00000000000..5183d00102b --- /dev/null +++ b/chromium/gin/per_context_data.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/per_context_data.h" + +#include "base/logging.h" +#include "gin/public/context_holder.h" +#include "gin/public/wrapper_info.h" + +namespace gin { + +ContextSupplement::ContextSupplement() { +} + +ContextSupplement::~ContextSupplement() { +} + +PerContextData::PerContextData(v8::Handle<v8::Context> context) + : runner_(NULL) { + context->SetAlignedPointerInEmbedderData( + kPerContextDataStartIndex + kEmbedderNativeGin, this); +} + +PerContextData::~PerContextData() { + DCHECK(supplements_.empty()); +} + +void PerContextData::Detach(v8::Handle<v8::Context> context) { + DCHECK(From(context) == this); + context->SetAlignedPointerInEmbedderData( + kPerContextDataStartIndex + kEmbedderNativeGin, NULL); + + SuplementVector supplements; + supplements.swap(supplements_); + + for (SuplementVector::iterator it = supplements.begin(); + it != supplements.end(); ++it) { + (*it)->Detach(context); + } +} + +PerContextData* PerContextData::From(v8::Handle<v8::Context> context) { + return static_cast<PerContextData*>( + context->GetAlignedPointerFromEmbedderData(kEncodedValueIndex)); +} + +void PerContextData::AddSupplement(scoped_ptr<ContextSupplement> supplement) { + supplements_.push_back(supplement.release()); +} + +} // namespace gin diff --git a/chromium/gin/per_context_data.h b/chromium/gin/per_context_data.h new file mode 100644 index 00000000000..3ad68d2d37c --- /dev/null +++ b/chromium/gin/per_context_data.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_PER_CONTEXT_DATA_H_ +#define GIN_PER_CONTEXT_DATA_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "gin/gin_export.h" +#include "v8/include/v8.h" + +namespace gin { + +class Runner; + +// Embedders can store additional per-context data by subclassing +// ContextSupplement. +class GIN_EXPORT ContextSupplement { + public: + ContextSupplement(); + virtual ~ContextSupplement(); + + // Detach will be called before ContextHolder disposes the v8::Context. + // Embedders should not interact with |context| after Detach has been called. + virtual void Detach(v8::Handle<v8::Context> context) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(ContextSupplement); +}; + +// There is one instance of PerContextData per v8::Context managed by Gin. This +// class stores all the Gin-related data that varies per context. +class GIN_EXPORT PerContextData { + public: + explicit PerContextData(v8::Handle<v8::Context> context); + ~PerContextData(); + + // Can return NULL after the ContextHolder has detached from context. + static PerContextData* From(v8::Handle<v8::Context>); + void Detach(v8::Handle<v8::Context> context); + + // The Runner associated with this context. To execute script in this context, + // please use the appropriate API on Runner. + Runner* runner() const { return runner_; } + void set_runner(Runner* runner) { runner_ = runner; } + + void AddSupplement(scoped_ptr<ContextSupplement> supplement); + + private: + typedef ScopedVector<ContextSupplement> SuplementVector; + + Runner* runner_; + SuplementVector supplements_; + + DISALLOW_COPY_AND_ASSIGN(PerContextData); +}; + +} // namespace gin + +#endif // GIN_PER_CONTEXT_DATA_H_ diff --git a/chromium/gin/per_isolate_data.cc b/chromium/gin/per_isolate_data.cc new file mode 100644 index 00000000000..6c2397ba50b --- /dev/null +++ b/chromium/gin/per_isolate_data.cc @@ -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. + +#include "gin/per_isolate_data.h" +#include "gin/public/gin_embedders.h" + +using v8::Eternal; +using v8::Isolate; +using v8::Local; +using v8::Object; +using v8::FunctionTemplate; +using v8::ObjectTemplate; + +namespace gin { + +PerIsolateData::PerIsolateData(Isolate* isolate) + : isolate_(isolate) { + 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_); +} + +} // namespace gin diff --git a/chromium/gin/per_isolate_data.h b/chromium/gin/per_isolate_data.h new file mode 100644 index 00000000000..ed935454749 --- /dev/null +++ b/chromium/gin/per_isolate_data.h @@ -0,0 +1,60 @@ +// 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 "gin/gin_export.h" +#include "gin/public/wrapper_info.h" +#include "v8/include/v8.h" + +namespace gin { + +// There is one instance of PerIsolateData per v8::Isolate managed by Gin. This +// class stores all the Gin-related data that varies per isolate. +class GIN_EXPORT PerIsolateData { + public: + explicit PerIsolateData(v8::Isolate* isolate); + ~PerIsolateData(); + + 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); + + v8::Isolate* isolate() { return isolate_; } + + private: + typedef std::map< + WrapperInfo*, v8::Eternal<v8::ObjectTemplate> > ObjectTemplateMap; + typedef std::map< + WrapperInfo*, v8::Eternal<v8::FunctionTemplate> > FunctionTemplateMap; + + // PerIsolateData doesn't actually own |isolate_|. Instead, the isolate is + // owned by the IsolateHolder, which also owns the PerIsolateData. + v8::Isolate* isolate_; + ObjectTemplateMap object_templates_; + FunctionTemplateMap function_templates_; + + DISALLOW_COPY_AND_ASSIGN(PerIsolateData); +}; + +} // namespace gin + +#endif // GIN_PER_ISOLATE_DATA_H_ diff --git a/chromium/gin/public/context_holder.h b/chromium/gin/public/context_holder.h new file mode 100644 index 00000000000..09fe248a373 --- /dev/null +++ b/chromium/gin/public/context_holder.h @@ -0,0 +1,54 @@ +// 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. Rather than +// using ContextHolder directly, most code should use a subclass of +// ContextHolder, such as Runner. +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::Persistent<v8::Context> context_; + scoped_ptr<PerContextData> data_; + + DISALLOW_COPY_AND_ASSIGN(ContextHolder); +}; + +} // namespace gin + +#endif // GIN_PUBLIC_CONTEXT_HOLDER_H_ diff --git a/chromium/gin/public/gin_embedders.h b/chromium/gin/public/gin_embedders.h new file mode 100644 index 00000000000..bffe6e0856e --- /dev/null +++ b/chromium/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/chromium/gin/public/isolate_holder.h b/chromium/gin/public/isolate_holder.h new file mode 100644 index 00000000000..d68e4d5a58b --- /dev/null +++ b/chromium/gin/public/isolate_holder.h @@ -0,0 +1,51 @@ +// 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" + +namespace v8 { +class Isolate; +} + +namespace gin { + +class PerIsolateData; + +// To embed Gin, first create an instance of IsolateHolder to hold the +// v8::Isolate in which you will execute JavaScript. You might wish to subclass +// IsolateHolder if you want to tie more state to the lifetime of the +// +// You can use gin in two modes: either gin manages V8, or the gin-embedder +// manages gin. If gin manages V8, use the IsolateHolder constructor without +// parameters, otherwise, the gin-embedder needs to create v8::Isolates and +// pass them to IsolateHolder. +// +// It is not possible to mix the two. +class GIN_EXPORT IsolateHolder { + public: + IsolateHolder(); + explicit IsolateHolder(v8::Isolate* isolate); + + ~IsolateHolder(); + + v8::Isolate* isolate() { return isolate_; } + + private: + void Init(); + + bool isolate_owner_; + v8::Isolate* isolate_; + scoped_ptr<PerIsolateData> isolate_data_; + + DISALLOW_COPY_AND_ASSIGN(IsolateHolder); +}; + +} // namespace gin + +#endif // GIN_PUBLIC_ISOLATE_HOLDER_H_ diff --git a/chromium/gin/public/wrapper_info.h b/chromium/gin/public/wrapper_info.h new file mode 100644 index 00000000000..31b2a981e3c --- /dev/null +++ b/chromium/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/chromium/gin/runner.cc b/chromium/gin/runner.cc new file mode 100644 index 00000000000..37e16a09c74 --- /dev/null +++ b/chromium/gin/runner.cc @@ -0,0 +1,102 @@ +// 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" + +#include "gin/converter.h" +#include "gin/per_context_data.h" +#include "gin/try_catch.h" + +using v8::Context; +using v8::HandleScope; +using v8::Isolate; +using v8::Object; +using v8::ObjectTemplate; +using v8::Script; + +namespace gin { + +RunnerDelegate::RunnerDelegate() { +} + +RunnerDelegate::~RunnerDelegate() { +} + +v8::Handle<ObjectTemplate> RunnerDelegate::GetGlobalTemplate(Runner* runner) { + return v8::Handle<ObjectTemplate>(); +} + +void RunnerDelegate::DidCreateContext(Runner* runner) { +} + +void RunnerDelegate::WillRunScript(Runner* runner) { +} + +void RunnerDelegate::DidRunScript(Runner* runner) { +} + +void RunnerDelegate::UnhandledException(Runner* runner, TryCatch& try_catch) { +} + +Runner::Runner(RunnerDelegate* delegate, Isolate* isolate) + : ContextHolder(isolate), + delegate_(delegate), + weak_factory_(this) { + v8::Isolate::Scope isolate_scope(isolate); + HandleScope handle_scope(isolate); + v8::Handle<v8::Context> context = + Context::New(isolate, NULL, delegate_->GetGlobalTemplate(this)); + + SetContext(context); + PerContextData::From(context)->set_runner(this); + + v8::Context::Scope scope(context); + delegate_->DidCreateContext(this); +} + +Runner::~Runner() { +} + +void Runner::Run(const std::string& source, const std::string& resource_name) { + Run(Script::New(StringToV8(isolate(), source), + StringToV8(isolate(), resource_name))); +} + +void Runner::Run(v8::Handle<Script> script) { + TryCatch try_catch; + delegate_->WillRunScript(this); + + script->Run(); + + delegate_->DidRunScript(this); + if (try_catch.HasCaught()) + delegate_->UnhandledException(this, try_catch); +} + +v8::Handle<v8::Value> Runner::Call(v8::Handle<v8::Function> function, + v8::Handle<v8::Value> receiver, + int argc, + v8::Handle<v8::Value> argv[]) { + TryCatch try_catch; + delegate_->WillRunScript(this); + + v8::Handle<v8::Value> result = function->Call(receiver, argc, argv); + + delegate_->DidRunScript(this); + if (try_catch.HasCaught()) + delegate_->UnhandledException(this, try_catch); + + return result; +} + +Runner::Scope::Scope(Runner* runner) + : isolate_scope_(runner->isolate()), + handle_scope_(runner->isolate()), + scope_(runner->context()) { +} + +Runner::Scope::~Scope() { +} + +} // namespace gin diff --git a/chromium/gin/runner.h b/chromium/gin/runner.h new file mode 100644 index 00000000000..943bcedba1c --- /dev/null +++ b/chromium/gin/runner.h @@ -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. + +#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" + +namespace gin { + +class Runner; +class TryCatch; + +// Subclass RunnerDelegate to customize the behavior of |Runner|. Typical +// embedders will want to subclass one of the specialized RunnerDelegates, +// such as ModuleRunnerDelegate. +class GIN_EXPORT RunnerDelegate { + public: + RunnerDelegate(); + virtual ~RunnerDelegate(); + + // Returns the template for the global object. + virtual v8::Handle<v8::ObjectTemplate> GetGlobalTemplate(Runner* runner); + virtual void DidCreateContext(Runner* runner); + virtual void WillRunScript(Runner* runner); + virtual void DidRunScript(Runner* runner); + virtual void UnhandledException(Runner* runner, TryCatch& try_catch); +}; + +// Runner lets you run code in a v8::Context. Upon construction, Runner will +// create a v8::Context. Upon destruction, Runner will dispose the context. +class GIN_EXPORT Runner : public ContextHolder { + public: + Runner(RunnerDelegate* delegate, v8::Isolate* isolate); + ~Runner(); + + // Before running script in this context, you'll need to enter the runner's + // context by creating an instance of Runner::Scope on the stack. + void Run(const std::string& source, const std::string& resource_name); + void Run(v8::Handle<v8::Script> script); + + v8::Handle<v8::Value> Call(v8::Handle<v8::Function> function, + v8::Handle<v8::Value> receiver, + int argc, + v8::Handle<v8::Value> argv[]); + + v8::Handle<v8::Object> global() const { + return context()->Global(); + } + + // 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; + + RunnerDelegate* delegate_; + + base::WeakPtrFactory<Runner> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(Runner); +}; + +} // namespace gin + +#endif // GIN_RUNNER_H_ diff --git a/chromium/gin/runner_unittest.cc b/chromium/gin/runner_unittest.cc new file mode 100644 index 00000000000..3723956df6a --- /dev/null +++ b/chromium/gin/runner_unittest.cc @@ -0,0 +1,37 @@ +// 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" + +#include "base/compiler_specific.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 instance; + + RunnerDelegate delegate; + Isolate* isolate = instance.isolate(); + Runner 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/chromium/gin/shell/gin_main.cc b/chromium/gin/shell/gin_main.cc new file mode 100644 index 00000000000..24fa6df1528 --- /dev/null +++ b/chromium/gin/shell/gin_main.cc @@ -0,0 +1,85 @@ +// 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/file_util.h" +#include "base/i18n/icu_util.h" +#include "base/message_loop/message_loop.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(file_util::GetCurrentDirectory(&module_base[0])); + return module_base; +} + +class ShellRunnerDelegate : public ModuleRunnerDelegate { + public: + ShellRunnerDelegate() : ModuleRunnerDelegate(GetModuleSearchPaths()) { + AddBuiltinModule(Console::kModuleName, Console::GetTemplate); + } + + virtual void UnhandledException(Runner* runner, + TryCatch& try_catch) OVERRIDE { + ModuleRunnerDelegate::UnhandledException(runner, try_catch); + LOG(ERROR) << try_catch.GetStackTrace(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(ShellRunnerDelegate); +}; + +} // namespace +} // namespace gin + +int main(int argc, char** argv) { + base::AtExitManager at_exit; + CommandLine::Init(argc, argv); + base::i18n::InitializeICU(); + + gin::IsolateHolder instance; + + base::MessageLoop message_loop; + + gin::ShellRunnerDelegate delegate; + gin::Runner 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/chromium/gin/try_catch.cc b/chromium/gin/try_catch.cc new file mode 100644 index 00000000000..a44e28e9fe5 --- /dev/null +++ b/chromium/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/chromium/gin/try_catch.h b/chromium/gin/try_catch.h new file mode 100644 index 00000000000..633b9092341 --- /dev/null +++ b/chromium/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/chromium/gin/wrappable.cc b/chromium/gin/wrappable.cc new file mode 100644 index 00000000000..4a9ef0e1935 --- /dev/null +++ b/chromium/gin/wrappable.cc @@ -0,0 +1,72 @@ +// 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/per_isolate_data.h" + +namespace gin { + +WrappableBase::WrappableBase() { +} + +WrappableBase::~WrappableBase() { + wrapper_.Reset(); +} + +v8::Handle<v8::Object> WrappableBase::GetWrapperImpl( + v8::Isolate* isolate, WrapperInfo* wrapper_info) { + if (wrapper_.IsEmpty()) + CreateWrapper(isolate, wrapper_info); + return v8::Local<v8::Object>::New(isolate, wrapper_); +} + +void WrappableBase::WeakCallback( + const v8::WeakCallbackData<v8::Object, WrappableBase>& data) { + WrappableBase* wrappable = data.GetParameter(); + wrappable->wrapper_.Reset(); + delete wrappable; +} + +v8::Handle<v8::Object> WrappableBase::CreateWrapper(v8::Isolate* isolate, + WrapperInfo* info) { + PerIsolateData* data = PerIsolateData::From(isolate); + v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate(info); + CHECK(!templ.IsEmpty()); // Don't forget to register an object template. + CHECK_EQ(kNumberOfInternalFields, templ->InternalFieldCount()); + v8::Handle<v8::Object> wrapper = templ->NewInstance(); + 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/chromium/gin/wrappable.h b/chromium/gin/wrappable.h new file mode 100644 index 00000000000..755707166bb --- /dev/null +++ b/chromium/gin/wrappable.h @@ -0,0 +1,101 @@ +// 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> { +// ... +// }; +// +// // my_class.cc +// INIT_WRAPABLE(MyClass); +// +// 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; + + +// Non-template base class to share code between templates instances. +class GIN_EXPORT WrappableBase { + protected: + WrappableBase(); + virtual ~WrappableBase(); + v8::Handle<v8::Object> GetWrapperImpl(v8::Isolate* isolate, + WrapperInfo* wrapper_info); + v8::Handle<v8::Object> CreateWrapper(v8::Isolate* isolate, + WrapperInfo* wrapper_info); + v8::Persistent<v8::Object> wrapper_; // Weak + + private: + static void WeakCallback( + const v8::WeakCallbackData<v8::Object, WrappableBase>& data); + + 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*, Wrappable<T>*>::value>::type> { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, T* val) { + return val->GetWrapper(isolate); + } + + static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val, T** out) { + *out = static_cast<T*>(internal::FromV8Impl(isolate, val, + &T::kWrapperInfo)); + return *out != NULL; + } +}; + +} // namespace gin + +#endif // GIN_WRAPPABLE_H_ diff --git a/chromium/gin/wrappable_unittest.cc b/chromium/gin/wrappable_unittest.cc new file mode 100644 index 00000000000..3499eedcc18 --- /dev/null +++ b/chromium/gin/wrappable_unittest.cc @@ -0,0 +1,149 @@ +// 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 MyObject : 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; } + + private: + MyObject() : value_(0) {} + virtual ~MyObject() {} + + int value_; +}; + +class MyObject2 : public Wrappable<MyObject2> { + public: + static WrapperInfo kWrapperInfo; +}; + +class MyObjectBlink : public Wrappable<MyObjectBlink> { + public: + static WrapperInfo kWrapperInfo; +}; + +WrapperInfo MyObject::kWrapperInfo = { kEmbedderNativeGin }; +WrapperInfo MyObject2::kWrapperInfo = { kEmbedderNativeGin }; +WrapperInfo MyObjectBlink::kWrapperInfo = { kEmbedderNativeGin }; + +void RegisterTemplates(v8::Isolate* isolate) { + PerIsolateData* data = PerIsolateData::From(isolate); + DCHECK(data->GetObjectTemplate(&MyObject::kWrapperInfo).IsEmpty()); + + v8::Handle<v8::ObjectTemplate> templ = ObjectTemplateBuilder(isolate) + .SetProperty("value", &MyObject::value, &MyObject::set_value) + .Build(); + templ->SetInternalFieldCount(kNumberOfInternalFields); + data->SetObjectTemplate(&MyObject::kWrapperInfo, templ); + + templ = v8::ObjectTemplate::New(isolate); + templ->SetInternalFieldCount(kNumberOfInternalFields); + data->SetObjectTemplate(&MyObject2::kWrapperInfo, templ); + + templ = v8::ObjectTemplate::New(isolate); + templ->SetInternalFieldCount(kNumberOfInternalFields); + data->SetObjectTemplate(&MyObjectBlink::kWrapperInfo, templ); +} + +typedef V8Test WrappableTest; + +TEST_F(WrappableTest, WrapAndUnwrap) { + v8::Isolate* isolate = instance_->isolate(); + v8::HandleScope handle_scope(isolate); + + RegisterTemplates(isolate); + Handle<MyObject> obj = MyObject::Create(isolate); + + v8::Handle<v8::Value> wrapper = ConvertToV8(isolate, obj.get()); + 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); + + RegisterTemplates(isolate); + + // Something that isn't an object. + v8::Handle<v8::Value> thing = v8::Number::New(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); + + RegisterTemplates(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::New(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()); +} + +} // namespace gin diff --git a/chromium/gin/wrapper_info.cc b/chromium/gin/wrapper_info.cc new file mode 100644 index 00000000000..6bf831696b0 --- /dev/null +++ b/chromium/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 |