summaryrefslogtreecommitdiffstats
path: root/gin
diff options
context:
space:
mode:
authorabarth@chromium.org <abarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-18 17:09:36 +0000
committerabarth@chromium.org <abarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-18 17:09:36 +0000
commit855ab43da4bd0342e2b8c9592b52ef80ac0b773f (patch)
tree0ea5aa300191aa3e0145fe083015b880edcaed53 /gin
parenta29e465aecb6cfc30b757d31242b06eb39846f2a (diff)
downloadchromium_src-855ab43da4bd0342e2b8c9592b52ef80ac0b773f.zip
chromium_src-855ab43da4bd0342e2b8c9592b52ef80ac0b773f.tar.gz
chromium_src-855ab43da4bd0342e2b8c9592b52ef80ac0b773f.tar.bz2
Introduce gin_shell
This CL adds a simple shell program for Gin to make edit/test/debug cycle faster. The shell excutes a list of scripts from the command line and loads any requested AMD modules relative to the current working directory. This CL will also let us remove the ugly code in https://codereview.chromium.org/69843003/diff/240001/mojo/public/bindings/js/test/run_js_tests.cc because we now know how to file modules via the file system. Eventually for Mojo, we'll want to use a net_module_provider (instead of the file_module_provider included in this CL) to load additional AMD modules off the network. BUG=317398 R=jochen@chromium.org Review URL: https://codereview.chromium.org/74753002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@235750 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gin')
-rw-r--r--gin/DEPS5
-rw-r--r--gin/arguments.cc7
-rw-r--r--gin/array_buffer.cc26
-rw-r--r--gin/array_buffer.h2
-rw-r--r--gin/context_holder.cc11
-rw-r--r--gin/context_holder.h5
-rw-r--r--gin/gin.gyp32
-rw-r--r--gin/initialize.cc2
-rw-r--r--gin/modules/file_module_provider.cc67
-rw-r--r--gin/modules/file_module_provider.h32
-rw-r--r--gin/modules/module_registry.cc190
-rw-r--r--gin/modules/module_registry.h40
-rw-r--r--gin/modules/module_runner_delegate.cc33
-rw-r--r--gin/modules/module_runner_delegate.h33
-rw-r--r--gin/per_context_data.cc11
-rw-r--r--gin/per_context_data.h9
-rw-r--r--gin/runner.cc29
-rw-r--r--gin/runner.h13
-rw-r--r--gin/runner_unittest.cc1
-rw-r--r--gin/shell/gin_main.cc74
-rw-r--r--gin/test/file_runner.cc37
-rw-r--r--gin/test/file_runner.h10
-rw-r--r--gin/try_catch.cc32
-rw-r--r--gin/try_catch.h31
24 files changed, 568 insertions, 164 deletions
diff --git a/gin/DEPS b/gin/DEPS
index 6e9bed8..4e3f30a 100644
--- a/gin/DEPS
+++ b/gin/DEPS
@@ -1,7 +1,4 @@
include_rules = [
- "+v8",
-
- # Use of base is allowed in tests. We can also use base in production code
- # as long as we don't introduce a link-time dependency.
"+base",
+ "+v8",
]
diff --git a/gin/arguments.cc b/gin/arguments.cc
index 0a2375c..9eedfa3 100644
--- a/gin/arguments.cc
+++ b/gin/arguments.cc
@@ -4,7 +4,7 @@
#include "gin/arguments.h"
-#include <sstream>
+#include "base/strings/stringprintf.h"
#include "gin/converter.h"
namespace gin {
@@ -29,9 +29,8 @@ void Arguments::ThrowError() {
if (insufficient_arguments_)
return ThrowTypeError("Insufficient number of arguments.");
- std::stringstream stream;
- stream << "Error processing argument " << next_ - 1 << ".";
- ThrowTypeError(stream.str());
+ ThrowTypeError(base::StringPrintf(
+ "Error processing argument %d.", next_ - 1));
}
void Arguments::ThrowTypeError(const std::string& message) {
diff --git a/gin/array_buffer.cc b/gin/array_buffer.cc
index 43dd8da..9f93bf9 100644
--- a/gin/array_buffer.cc
+++ b/gin/array_buffer.cc
@@ -53,26 +53,25 @@ ArrayBufferAllocator* ArrayBufferAllocator::SharedInstance() {
// a pointer to the ArrayBuffer::Private object in an internal field of the
// ArrayBuffer object.
//
-class ArrayBuffer::Private {
+class ArrayBuffer::Private : public base::RefCounted<ArrayBuffer::Private> {
public:
static scoped_refptr<Private> From(v8::Isolate* isolate,
v8::Handle<v8::ArrayBuffer> array);
- void AddRef();
- void Release();
-
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);
- size_t ref_count_;
v8::Persistent<v8::ArrayBuffer> array_buffer_;
+ scoped_refptr<Private> self_reference_;
void* buffer_;
size_t length_;
};
@@ -86,20 +85,9 @@ scoped_refptr<ArrayBuffer::Private> ArrayBuffer::Private::From(
return make_scoped_refptr(new Private(isolate, array));
}
-void ArrayBuffer::Private::AddRef() {
- ++ref_count_;
-}
-
-void ArrayBuffer::Private::Release() {
- if (--ref_count_)
- return;
- delete this;
-}
-
ArrayBuffer::Private::Private(v8::Isolate* isolate,
v8::Handle<v8::ArrayBuffer> array)
- : ref_count_(0),
- array_buffer_(isolate, array) {
+ : array_buffer_(isolate, array) {
// Take ownership of the array buffer.
v8::ArrayBuffer::Contents contents = array->Externalize();
buffer_ = contents.Data();
@@ -107,7 +95,7 @@ ArrayBuffer::Private::Private(v8::Isolate* isolate,
array->SetAlignedPointerInInternalField(kBufferViewPrivateIndex, this);
- AddRef(); // Balanced in WeakCallback.
+ self_reference_ = this; // Cleared in WeakCallback.
array_buffer_.SetWeak(this, WeakCallback);
}
@@ -119,7 +107,7 @@ void ArrayBuffer::Private::WeakCallback(
const v8::WeakCallbackData<v8::ArrayBuffer, Private>& data) {
Private* parameter = data.GetParameter();
parameter->array_buffer_.Reset();
- parameter->Release(); // Balanced in ArrayBuffer::Private::Private.
+ // parameter->self_reference_.clear();
}
// ArrayBuffer ----------------------------------------------------------------
diff --git a/gin/array_buffer.h b/gin/array_buffer.h
index a645431..30d45f9 100644
--- a/gin/array_buffer.h
+++ b/gin/array_buffer.h
@@ -7,7 +7,7 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
-#include "base/memory/ref_counted.h" // For scoped_refptr only!
+#include "base/memory/ref_counted.h"
#include "gin/converter.h"
#include "v8/include/v8.h"
diff --git a/gin/context_holder.cc b/gin/context_holder.cc
index 077feb7..80c1198 100644
--- a/gin/context_holder.cc
+++ b/gin/context_holder.cc
@@ -4,7 +4,7 @@
#include "gin/context_holder.h"
-#include <assert.h>
+#include "base/logging.h"
#include "gin/per_context_data.h"
namespace gin {
@@ -17,18 +17,17 @@ ContextHolder::~ContextHolder() {
v8::HandleScope handle_scope(isolate());
v8::Handle<v8::Context> context = this->context();
- PerContextData* data = PerContextData::From(context);
- data->Detach(context);
- delete data;
+ 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) {
- assert(context_.IsEmpty());
+ DCHECK(context_.IsEmpty());
context_.Reset(isolate_, context);
- new PerContextData(context); // Deleted in ~ContextHolder.
+ data_.reset(new PerContextData(context));
}
} // namespace gin
diff --git a/gin/context_holder.h b/gin/context_holder.h
index c9068d1..d43e193 100644
--- a/gin/context_holder.h
+++ b/gin/context_holder.h
@@ -6,11 +6,15 @@
#define GIN_CONTEXT_HOLDER_H_
#include <list>
+
#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
#include "v8/include/v8.h"
namespace gin {
+class PerContextData;
+
class ContextHolder {
public:
explicit ContextHolder(v8::Isolate* isolate);
@@ -27,6 +31,7 @@ class ContextHolder {
private:
v8::Isolate* isolate_;
v8::Persistent<v8::Context> context_;
+ scoped_ptr<PerContextData> data_;
DISALLOW_COPY_AND_ASSIGN(ContextHolder);
};
diff --git a/gin/gin.gyp b/gin/gin.gyp
index 597482a..7bdf8ab 100644
--- a/gin/gin.gyp
+++ b/gin/gin.gyp
@@ -10,26 +10,29 @@
{
'target_name': 'gin',
'type': 'static_library',
- 'include_dirs': [
- '..'
- ],
'dependencies': [
+ '../base/base.gyp:base',
'../v8/tools/gyp/v8.gyp:v8',
],
'export_dependent_settings': [
+ '../base/base.gyp:base',
'../v8/tools/gyp/v8.gyp:v8',
],
'sources': [
+ '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',
'arguments.cc',
'arguments.h',
'array_buffer.cc',
'array_buffer.h',
- 'converter.cc',
- 'converter.h',
'context_holder.cc',
'context_holder.h',
+ 'converter.cc',
+ 'converter.h',
'dictionary.cc',
'dictionary.h',
'initialize.cc',
@@ -40,20 +43,35 @@
'per_isolate_data.h',
'runner.cc',
'runner.h',
+ 'try_catch.cc',
+ 'try_catch.h',
'wrapper_info.cc',
'wrapper_info.h',
],
},
{
+ 'target_name': 'gin_shell',
+ 'type': 'executable',
+ 'dependencies': [
+ 'gin',
+ ],
+ 'sources': [
+ 'shell/gin_main.cc',
+ ],
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'SubSystem': '1', # /SUBSYSTEM:CONSOLE
+ },
+ },
+ },
+ {
'target_name': 'gin_test',
'type': 'static_library',
'dependencies': [
- '../base/base.gyp:base',
'../testing/gtest.gyp:gtest',
'gin',
],
'export_dependent_settings': [
- '../base/base.gyp:base',
'../testing/gtest.gyp:gtest',
'gin',
],
diff --git a/gin/initialize.cc b/gin/initialize.cc
index 64b5908..d95470f 100644
--- a/gin/initialize.cc
+++ b/gin/initialize.cc
@@ -27,7 +27,7 @@ void Initialize() {
v8::V8::SetArrayBufferAllocator(ArrayBufferAllocator::SharedInstance());
v8::V8::InitializeICU();
v8::V8::SetFlagsFromString(kFlags,
- static_cast<uint32_t>(sizeof(kFlags) / sizeof(kFlags[0])) - 1);
+ static_cast<uint32_t>(arraysize(kFlags)) - 1);
v8::V8::SetEntropySource(&GenerateEntropy);
v8::V8::Initialize();
diff --git a/gin/modules/file_module_provider.cc b/gin/modules/file_module_provider.cc
new file mode 100644
index 0000000..0a3e24c
--- /dev/null
+++ b/gin/modules/file_module_provider.cc
@@ -0,0 +1,67 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gin/modules/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 base::FilePath& base,
+ const std::string& id) {
+ if (!runner)
+ return;
+
+ std::vector<std::string> components;
+ base::SplitString(id, '/', &components);
+ base::FilePath path = base;
+ 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"));
+
+ std::string source;
+ if (!ReadFileToString(path, &source))
+ return;
+
+ Runner::Scope scope(runner.get());
+ v8::Handle<v8::Script> script = v8::Script::New(
+ StringToV8(runner->isolate(), source),
+ StringToV8(runner->isolate(), id));
+ runner->Run(script);
+}
+
+} // namespace
+
+FileModuleProvider::FileModuleProvider(const base::FilePath& base)
+ : base_(base) {
+}
+
+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(), base_, id));
+ }
+}
+
+} // namespace gin
diff --git a/gin/modules/file_module_provider.h b/gin/modules/file_module_provider.h
new file mode 100644
index 0000000..1e6fb88e
--- /dev/null
+++ b/gin/modules/file_module_provider.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_MODULES_FILE_MODULE_PROVIDER_H_
+#define GIN_MODULES_FILE_MODULE_PROVIDER_H_
+
+#include <set>
+#include <string>
+
+#include "base/files/file_path.h"
+#include "gin/runner.h"
+
+namespace gin {
+
+class FileModuleProvider {
+ public:
+ explicit FileModuleProvider(const base::FilePath& base);
+ ~FileModuleProvider();
+
+ void AttempToLoadModules(Runner* runner, const std::set<std::string>& ids);
+
+ private:
+ base::FilePath base_;
+ std::set<std::string> attempted_ids_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileModuleProvider);
+};
+
+} // namespace gin
+
+#endif // GIN_MODULES_FILE_MODULE_PROVIDER_H_
diff --git a/gin/modules/module_registry.cc b/gin/modules/module_registry.cc
index bfa05a1..20ee287 100644
--- a/gin/modules/module_registry.cc
+++ b/gin/modules/module_registry.cc
@@ -4,18 +4,28 @@
#include "gin/modules/module_registry.h"
-#include <assert.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/wrapper_info.h"
+using v8::Context;
using v8::External;
+using v8::Function;
+using v8::FunctionTemplate;
using v8::Handle;
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 {
@@ -25,12 +35,19 @@ struct PendingModule {
std::string id;
std::vector<std::string> dependencies;
- v8::Persistent<v8::Value> factory;
+ Persistent<Value> factory;
};
+PendingModule::PendingModule() {
+}
+
+PendingModule::~PendingModule() {
+ factory.Reset();
+}
+
namespace {
-void Define(const v8::FunctionCallbackInfo<v8::Value>& info) {
+void Define(const v8::FunctionCallbackInfo<Value>& info) {
Arguments args(info);
if (!info.Length())
@@ -38,7 +55,7 @@ void Define(const v8::FunctionCallbackInfo<v8::Value>& info) {
std::string id;
std::vector<std::string> dependencies;
- Handle<v8::Value> factory;
+ Handle<Value> factory;
if (args.PeekNext()->IsString())
args.GetNext(&id);
@@ -47,142 +64,165 @@ void Define(const v8::FunctionCallbackInfo<v8::Value>& info) {
if (!args.GetNext(&factory))
return args.ThrowError();
- PendingModule* pending = new PendingModule;
+ 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);
+ registry->AddPendingModule(args.isolate(), pending.Pass());
}
WrapperInfo g_wrapper_info = {};
-v8::Local<v8::FunctionTemplate> GetDefineTemplate(v8::Isolate* isolate) {
+Local<FunctionTemplate> GetDefineTemplate(Isolate* isolate) {
PerIsolateData* data = PerIsolateData::From(isolate);
- v8::Local<v8::FunctionTemplate> templ = data->GetFunctionTemplate(
+ Local<FunctionTemplate> templ = data->GetFunctionTemplate(
&g_wrapper_info);
if (templ.IsEmpty()) {
- templ = v8::FunctionTemplate::New(Define);
+ templ = FunctionTemplate::New(Define);
data->SetFunctionTemplate(&g_wrapper_info, templ);
}
return templ;
}
-Handle<v8::String> GetHiddenValueKey(v8::Isolate* isolate) {
+Handle<String> GetHiddenValueKey(Isolate* isolate) {
return StringToSymbol(isolate, "::gin::ModuleRegistry");
}
-} // namespace
-
-
-PendingModule::PendingModule() {
+std::string GetImplicitModuleName(const std::string& explicit_name) {
+ if (!explicit_name.empty())
+ return explicit_name;
+ std::string implicit_name;
+ Handle<StackTrace> trace = StackTrace::CurrentStackTrace(1);
+ Handle<String> script_name = trace->GetFrame(0)->GetScriptName();
+ if (!script_name.IsEmpty())
+ ConvertFromV8(script_name, &implicit_name);
+ return implicit_name;
}
-PendingModule::~PendingModule() {
- factory.Reset();
-}
+} // namespace
-ModuleRegistry::ModuleRegistry(v8::Isolate* isolate)
- : modules_(isolate, v8::Object::New()) {
+ModuleRegistry::ModuleRegistry(Isolate* isolate)
+ : modules_(isolate, Object::New()) {
}
ModuleRegistry::~ModuleRegistry() {
- for (PendingModuleList::iterator it = pending_modules_.begin();
- it != pending_modules_.end(); ++it) {
- delete *it;
- }
modules_.Reset();
}
-void ModuleRegistry::RegisterGlobals(v8::Isolate* isolate,
- Handle<v8::ObjectTemplate> templ) {
+void ModuleRegistry::RegisterGlobals(Isolate* isolate,
+ Handle<ObjectTemplate> templ) {
templ->Set(StringToSymbol(isolate, "define"), GetDefineTemplate(isolate));
}
-void ModuleRegistry::AddBuiltinModule(Isolate* isolate,
- const std::string& id,
- Handle<ObjectTemplate> templ) {
- assert(!id.empty());
- Handle<v8::Object> modules = v8::Local<v8::Object>::New(isolate, modules_);
- modules->Set(StringToV8(isolate, id), templ->NewInstance());
-}
-
-ModuleRegistry* ModuleRegistry::From(Handle<v8::Context> context) {
- v8::Isolate* isolate = context->GetIsolate();
- Handle<v8::String> key = GetHiddenValueKey(isolate);
- Handle<v8::Value> value = context->Global()->GetHiddenValue(key);
- Handle<v8::External> external;
+ModuleRegistry* ModuleRegistry::From(Handle<Context> context) {
+ Isolate* isolate = context->GetIsolate();
+ Handle<String> key = GetHiddenValueKey(isolate);
+ Handle<Value> value = context->Global()->GetHiddenValue(key);
+ Handle<External> external;
if (value.IsEmpty() || !ConvertFromV8(value, &external)) {
PerContextData* data = PerContextData::From(context);
if (!data)
return NULL;
ModuleRegistry* registry = new ModuleRegistry(isolate);
- context->Global()->SetHiddenValue(key,
- v8::External::New(isolate, registry));
- data->AddSupplement(registry);
+ context->Global()->SetHiddenValue(key, External::New(isolate, registry));
+ data->AddSupplement(scoped_ptr<ContextSupplement>(registry));
return registry;
}
return static_cast<ModuleRegistry*>(external->Value());
}
-void ModuleRegistry::AddPendingModule(v8::Isolate* isolate,
- PendingModule* pending) {
- if (AttemptToLoad(isolate, pending))
- AttemptToLoadPendingModules(isolate);
+void ModuleRegistry::AddBuiltinModule(Isolate* isolate,
+ const std::string& id,
+ Handle<ObjectTemplate> templ) {
+ DCHECK(!id.empty());
+ RegisterModule(isolate, id, templ->NewInstance());
}
-void ModuleRegistry::Detach(Handle<v8::Context> context) {
- context->Global()->SetHiddenValue(GetHiddenValueKey(context->GetIsolate()),
- Handle<v8::Value>());
+void ModuleRegistry::AddPendingModule(Isolate* isolate,
+ scoped_ptr<PendingModule> pending) {
+ AttemptToLoad(isolate, pending.Pass());
+}
+
+void ModuleRegistry::RegisterModule(Isolate* isolate,
+ const std::string& id,
+ Handle<Value> module) {
+ if (id.empty() || module.IsEmpty())
+ return;
+
+ unsatisfied_dependencies_.erase(id);
+ available_modules_.insert(id);
+ Handle<Object> modules = Local<Object>::New(isolate, modules_);
+ modules->Set(StringToSymbol(isolate, id), module);
}
-bool ModuleRegistry::AttemptToLoad(v8::Isolate* isolate,
- PendingModule* pending) {
- Handle<v8::Object> modules = v8::Local<v8::Object>::New(isolate, modules_);
- Handle<v8::String> key = StringToV8(isolate, pending->id);
+void ModuleRegistry::Detach(Handle<Context> context) {
+ context->Global()->SetHiddenValue(GetHiddenValueKey(context->GetIsolate()),
+ Handle<Value>());
+}
- if (!pending->id.empty() && modules->HasOwnProperty(key)) {
- // We've already loaded a module with this name. Ignore the new one.
- delete pending;
- return true;
+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.
+ Handle<Object> modules = Local<Object>::New(isolate, modules_);
size_t argc = pending->dependencies.size();
- std::vector<Handle<v8::Value> > argv(argc);
+ std::vector<Handle<Value> > argv(argc);
for (size_t i = 0; i < argc; ++i) {
- Handle<v8::String> key = StringToV8(isolate, pending->dependencies[i]);
- if (!modules->HasOwnProperty(key)) {
- pending_modules_.push_back(pending);
- return false;
- }
+ Handle<String> key = StringToSymbol(isolate, pending->dependencies[i]);
+ DCHECK(modules->HasOwnProperty(key));
argv[i] = modules->Get(key);
}
- Handle<v8::Value> module = v8::Local<v8::Value>::New(
- isolate, pending->factory);
+ Handle<Value> module = Local<Value>::New(isolate, pending->factory);
- Handle<v8::Function> factory;
+ Handle<Function> factory;
if (ConvertFromV8(module, &factory)) {
- v8::Handle<v8::Object> global = isolate->GetCurrentContext()->Global();
+ Handle<Object> global = isolate->GetCurrentContext()->Global();
module = factory->Call(global, argc, argv.data());
// TODO(abarth): What should we do with exceptions?
}
- if (!pending->id.empty() && !module.IsEmpty())
- modules->Set(key, module);
+ RegisterModule(isolate, GetImplicitModuleName(pending->id), module);
+}
- delete pending;
+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;
}
-void ModuleRegistry::AttemptToLoadPendingModules(v8::Isolate* isolate) {
- PendingModuleList pending_modules;
- pending_modules.swap(pending_modules_);
- for (PendingModuleList::iterator it = pending_modules.begin();
- it != pending_modules.end(); ++it) {
- AttemptToLoad(isolate, *it);
+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;
+ }
}
}
diff --git a/gin/modules/module_registry.h b/gin/modules/module_registry.h
index 7cff355..a7732c8 100644
--- a/gin/modules/module_registry.h
+++ b/gin/modules/module_registry.h
@@ -6,8 +6,11 @@
#define GIN_MODULES_MODULE_REGISTRY_H_
#include <list>
+#include <set>
#include <string>
+
#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
#include "gin/per_context_data.h"
namespace gin {
@@ -27,6 +30,8 @@ struct PendingModule;
//
class ModuleRegistry : public ContextSupplement {
public:
+ virtual ~ModuleRegistry();
+
static ModuleRegistry* From(v8::Handle<v8::Context> context);
static void RegisterGlobals(v8::Isolate* isolate,
@@ -37,25 +42,42 @@ class ModuleRegistry : public ContextSupplement {
const std::string& id,
v8::Handle<v8::ObjectTemplate> templ);
- // Takes ownership of |pending|. The caller must have already entered
- // our context.
- void AddPendingModule(v8::Isolate* isolate, PendingModule* pending);
+ // The caller must have already entered our context.
+ void AddPendingModule(v8::Isolate* isolate,
+ scoped_ptr<PendingModule> pending);
+
+ // 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 std::list<PendingModule*> PendingModuleList; // Owning reference.
+ typedef ScopedVector<PendingModule> PendingModuleVector;
explicit ModuleRegistry(v8::Isolate* isolate);
- virtual ~ModuleRegistry();
// From ContextSupplement:
virtual void Detach(v8::Handle<v8::Context> context) OVERRIDE;
- // Takes ownership of |pending|.
- bool AttemptToLoad(v8::Isolate* isolate, PendingModule* pending);
- void AttemptToLoadPendingModules(v8::Isolate* isolate);
+ void Load(v8::Isolate* isolate, scoped_ptr<PendingModule> pending);
+ void RegisterModule(v8::Isolate* isolate,
+ const std::string& id,
+ v8::Handle<v8::Value> module);
+
+ bool CheckDependencies(PendingModule* pending);
+ bool AttemptToLoad(v8::Isolate* isolate, scoped_ptr<PendingModule> pending);
+
+ std::set<std::string> available_modules_;
+ std::set<std::string> unsatisfied_dependencies_;
+ PendingModuleVector pending_modules_;
v8::Persistent<v8::Object> modules_;
- PendingModuleList pending_modules_;
DISALLOW_COPY_AND_ASSIGN(ModuleRegistry);
};
diff --git a/gin/modules/module_runner_delegate.cc b/gin/modules/module_runner_delegate.cc
new file mode 100644
index 0000000..e029a93
--- /dev/null
+++ b/gin/modules/module_runner_delegate.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/modules/module_runner_delegate.h"
+
+#include "gin/modules/module_registry.h"
+
+namespace gin {
+
+ModuleRunnerDelegate::ModuleRunnerDelegate(const base::FilePath& module_base)
+ : module_provider_(module_base) {
+}
+
+ModuleRunnerDelegate::~ModuleRunnerDelegate() {
+}
+
+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::DidRunScript(Runner* runner,
+ v8::Handle<v8::Script> script) {
+ ModuleRegistry* registry = ModuleRegistry::From(runner->context());
+ registry->AttemptToLoadMoreModules(runner->isolate());
+ module_provider_.AttempToLoadModules(
+ runner, registry->unsatisfied_dependencies());
+}
+
+} // namespace gin
diff --git a/gin/modules/module_runner_delegate.h b/gin/modules/module_runner_delegate.h
new file mode 100644
index 0000000..f16596e
--- /dev/null
+++ b/gin/modules/module_runner_delegate.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_MODULES_MODULE_RUNNER_DELEGATE_H_
+#define GIN_MODULES_MODULE_RUNNER_DELEGATE_H_
+
+#include "base/compiler_specific.h"
+#include "gin/modules/file_module_provider.h"
+#include "gin/runner.h"
+
+namespace gin {
+
+class ModuleRunnerDelegate : public RunnerDelegate {
+ public:
+ explicit ModuleRunnerDelegate(const base::FilePath& module_base);
+ virtual ~ModuleRunnerDelegate();
+
+ private:
+ // From RunnerDelegate:
+ virtual v8::Handle<v8::ObjectTemplate> GetGlobalTemplate(
+ Runner* runner) OVERRIDE;
+ virtual void DidRunScript(Runner* runner,
+ v8::Handle<v8::Script> script) OVERRIDE;
+
+ FileModuleProvider module_provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModuleRunnerDelegate);
+};
+
+} // namespace gin
+
+#endif // GIN_MODULES_MODULE_RUNNER_DELEGATE_H_
diff --git a/gin/per_context_data.cc b/gin/per_context_data.cc
index 8f6261a..58a7241 100644
--- a/gin/per_context_data.cc
+++ b/gin/per_context_data.cc
@@ -4,7 +4,7 @@
#include "gin/per_context_data.h"
-#include <assert.h>
+#include "base/logging.h"
#include "gin/wrapper_info.h"
namespace gin {
@@ -20,11 +20,11 @@ PerContextData::PerContextData(v8::Handle<v8::Context> context) {
}
PerContextData::~PerContextData() {
- assert(supplements_.empty());
+ DCHECK(supplements_.empty());
}
void PerContextData::Detach(v8::Handle<v8::Context> context) {
- assert(From(context) == this);
+ DCHECK(From(context) == this);
context->SetAlignedPointerInEmbedderData(kEncodedValueIndex, NULL);
SuplementVector supplements;
@@ -33,7 +33,6 @@ void PerContextData::Detach(v8::Handle<v8::Context> context) {
for (SuplementVector::iterator it = supplements.begin();
it != supplements.end(); ++it) {
(*it)->Detach(context);
- delete *it;
}
}
@@ -42,8 +41,8 @@ PerContextData* PerContextData::From(v8::Handle<v8::Context> context) {
context->GetAlignedPointerFromEmbedderData(kEncodedValueIndex));
}
-void PerContextData::AddSupplement(ContextSupplement* supplement) {
- supplements_.push_back(supplement);
+void PerContextData::AddSupplement(scoped_ptr<ContextSupplement> supplement) {
+ supplements_.push_back(supplement.release());
}
} // namespace gin
diff --git a/gin/per_context_data.h b/gin/per_context_data.h
index 123db59..0f10485 100644
--- a/gin/per_context_data.h
+++ b/gin/per_context_data.h
@@ -5,8 +5,9 @@
#ifndef GIN_PER_CONTEXT_DATA_H_
#define GIN_PER_CONTEXT_DATA_H_
-#include <vector>
#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
#include "v8/include/v8.h"
namespace gin {
@@ -31,13 +32,11 @@ class PerContextData {
static PerContextData* From(v8::Handle<v8::Context>);
void Detach(v8::Handle<v8::Context> context);
- // Takes ownership of the supplement.
- void AddSupplement(ContextSupplement* supplement);
+ void AddSupplement(scoped_ptr<ContextSupplement> supplement);
private:
- typedef std::vector<ContextSupplement*> SuplementVector;
+ typedef ScopedVector<ContextSupplement> SuplementVector;
- // Owning reference.
SuplementVector supplements_;
DISALLOW_COPY_AND_ASSIGN(PerContextData);
diff --git a/gin/runner.cc b/gin/runner.cc
index 4b0b6f9..a3c9544 100644
--- a/gin/runner.cc
+++ b/gin/runner.cc
@@ -5,9 +5,9 @@
#include "gin/runner.h"
#include "gin/converter.h"
+#include "gin/try_catch.h"
using v8::Context;
-using v8::Handle;
using v8::HandleScope;
using v8::Isolate;
using v8::Object;
@@ -22,16 +22,26 @@ RunnerDelegate::RunnerDelegate() {
RunnerDelegate::~RunnerDelegate() {
}
-Handle<ObjectTemplate> RunnerDelegate::GetGlobalTemplate(Runner* runner) {
- return Handle<ObjectTemplate>();
+v8::Handle<ObjectTemplate> RunnerDelegate::GetGlobalTemplate(Runner* runner) {
+ return v8::Handle<ObjectTemplate>();
}
void RunnerDelegate::DidCreateContext(Runner* runner) {
}
+void RunnerDelegate::WillRunScript(Runner* runner, v8::Handle<Script> script) {
+}
+
+void RunnerDelegate::DidRunScript(Runner* runner, v8::Handle<Script> script) {
+}
+
+void RunnerDelegate::UnhandledException(Runner* runner, TryCatch& try_catch) {
+}
+
Runner::Runner(RunnerDelegate* delegate, Isolate* isolate)
: ContextHolder(isolate),
- delegate_(delegate) {
+ delegate_(delegate),
+ weak_factory_(this) {
HandleScope handle_scope(isolate);
SetContext(Context::New(isolate, NULL, delegate_->GetGlobalTemplate(this)));
@@ -46,8 +56,15 @@ void Runner::Run(const std::string& script) {
Run(Script::New(StringToV8(isolate(), script)));
}
-void Runner::Run(Handle<Script> script) {
- script->Run();
+void Runner::Run(v8::Handle<Script> script) {
+ delegate_->WillRunScript(this, script);
+ {
+ TryCatch try_catch;
+ script->Run();
+ if (try_catch.HasCaught())
+ delegate_->UnhandledException(this, try_catch);
+ }
+ delegate_->DidRunScript(this, script);
}
Runner::Scope::Scope(Runner* runner)
diff --git a/gin/runner.h b/gin/runner.h
index 3e999e1f..fa41259 100644
--- a/gin/runner.h
+++ b/gin/runner.h
@@ -6,11 +6,14 @@
#define GIN_RUNNER_H_
#include <string>
+
+#include "base/memory/weak_ptr.h"
#include "gin/context_holder.h"
namespace gin {
class Runner;
+class TryCatch;
class RunnerDelegate {
public:
@@ -19,8 +22,10 @@ class 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, v8::Handle<v8::Script> script);
+ virtual void DidRunScript(Runner* runner, v8::Handle<v8::Script> script);
+ virtual void UnhandledException(Runner* runner, TryCatch& try_catch);
};
class Runner : public ContextHolder {
@@ -35,6 +40,10 @@ class Runner : public ContextHolder {
return context()->Global();
}
+ base::WeakPtr<Runner> GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+ }
+
class Scope {
public:
explicit Scope(Runner* runner);
@@ -52,6 +61,8 @@ class Runner : public ContextHolder {
RunnerDelegate* delegate_;
+ base::WeakPtrFactory<Runner> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(Runner);
};
diff --git a/gin/runner_unittest.cc b/gin/runner_unittest.cc
index a95a113..adcebed 100644
--- a/gin/runner_unittest.cc
+++ b/gin/runner_unittest.cc
@@ -8,7 +8,6 @@
#include "gin/converter.h"
#include "testing/gtest/include/gtest/gtest.h"
-using v8::Handle;
using v8::Isolate;
using v8::Object;
using v8::Script;
diff --git a/gin/shell/gin_main.cc b/gin/shell/gin_main.cc
new file mode 100644
index 0000000..5d2831c
--- /dev/null
+++ b/gin/shell/gin_main.cc
@@ -0,0 +1,74 @@
+// 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/message_loop/message_loop.h"
+#include "gin/initialize.h"
+#include "gin/modules/module_runner_delegate.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 std::string& source) {
+ if (!runner)
+ return;
+ Runner::Scope scope(runner.get());
+ runner->Run(source);
+}
+
+base::FilePath GetModuleBase() {
+ base::FilePath module_base;
+ CHECK(file_util::GetCurrentDirectory(&module_base));
+ return module_base;
+}
+
+class ShellRunnerDelegate : public ModuleRunnerDelegate {
+ public:
+ ShellRunnerDelegate() : ModuleRunnerDelegate(GetModuleBase()) {}
+
+ virtual void UnhandledException(Runner* runner,
+ TryCatch& try_catch) OVERRIDE {
+ ModuleRunnerDelegate::UnhandledException(runner, try_catch);
+ LOG(ERROR) << try_catch.GetPrettyMessage();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ShellRunnerDelegate);
+};
+
+} // namespace
+} // namespace gin
+
+int main(int argc, char** argv) {
+ base::AtExitManager at_exit;
+ CommandLine::Init(argc, argv);
+ gin::Initialize();
+
+ base::MessageLoop message_loop;
+
+ gin::ShellRunnerDelegate delegate;
+ gin::Runner runner(&delegate, v8::Isolate::GetCurrent());
+
+ 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(), gin::Load(base::FilePath(*it))));
+ }
+
+ message_loop.RunUntilIdle();
+ return 0;
+}
diff --git a/gin/test/file_runner.cc b/gin/test/file_runner.cc
index a8d0cc2..a343d64 100644
--- a/gin/test/file_runner.cc
+++ b/gin/test/file_runner.cc
@@ -5,37 +5,36 @@
#include "gin/test/file_runner.h"
#include "base/file_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.h"
#include "gin/converter.h"
#include "gin/modules/module_registry.h"
#include "gin/test/gtest.h"
+#include "gin/try_catch.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace gin {
namespace {
-std::string GetExceptionInfo(const v8::TryCatch& try_catch) {
- std::string info;
- ConvertFromV8(try_catch.Message()->Get(), &info);
- return info;
+base::FilePath GetModuleBase() {
+ base::FilePath path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ return path;
}
} // namespace
-FileRunnerDelegate::FileRunnerDelegate() {
+FileRunnerDelegate::FileRunnerDelegate()
+ : ModuleRunnerDelegate(GetModuleBase()) {
}
FileRunnerDelegate::~FileRunnerDelegate() {
}
-v8::Handle<v8::ObjectTemplate> FileRunnerDelegate::GetGlobalTemplate(
- Runner* runner) {
- v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
- ModuleRegistry::RegisterGlobals(runner->isolate(), templ);
- return templ;
-}
-
void FileRunnerDelegate::DidCreateContext(Runner* runner) {
+ ModuleRunnerDelegate::DidCreateContext(runner);
+
v8::Handle<v8::Context> context = runner->context();
ModuleRegistry* registry = ModuleRegistry::From(context);
@@ -43,16 +42,24 @@ void FileRunnerDelegate::DidCreateContext(Runner* runner) {
GetGTestTemplate(runner->isolate()));
}
+void FileRunnerDelegate::UnhandledException(Runner* runner,
+ TryCatch& try_catch) {
+ ModuleRunnerDelegate::UnhandledException(runner, try_catch);
+ EXPECT_FALSE(try_catch.HasCaught()) << try_catch.GetPrettyMessage();
+}
+
void RunTestFromFile(const base::FilePath& path, RunnerDelegate* delegate) {
ASSERT_TRUE(base::PathExists(path)) << path.LossyDisplayName();
std::string source;
ASSERT_TRUE(ReadFileToString(path, &source));
+
+ base::MessageLoop message_loop;
+
gin::Runner runner(delegate, v8::Isolate::GetCurrent());
gin::Runner::Scope scope(&runner);
-
- v8::TryCatch try_catch;
runner.Run(source);
- EXPECT_FALSE(try_catch.HasCaught()) << GetExceptionInfo(try_catch);
+
+ message_loop.RunUntilIdle();
v8::Handle<v8::Value> result = runner.context()->Global()->Get(
StringToSymbol(runner.isolate(), "result"));
diff --git a/gin/test/file_runner.h b/gin/test/file_runner.h
index b4ca00e..b00e089 100644
--- a/gin/test/file_runner.h
+++ b/gin/test/file_runner.h
@@ -7,19 +7,21 @@
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
+#include "gin/modules/module_runner_delegate.h"
#include "gin/runner.h"
namespace gin {
-class FileRunnerDelegate : public RunnerDelegate {
+class FileRunnerDelegate : public ModuleRunnerDelegate {
public:
FileRunnerDelegate();
virtual ~FileRunnerDelegate();
- virtual v8::Handle<v8::ObjectTemplate> GetGlobalTemplate(
- Runner* runner) OVERRIDE;
- virtual void DidCreateContext(Runner* runner) OVERRIDE;
private:
+ // From ModuleRunnerDelegate:
+ virtual void DidCreateContext(Runner* runner) OVERRIDE;
+ virtual void UnhandledException(Runner* runner, TryCatch& try_catch) OVERRIDE;
+
DISALLOW_COPY_AND_ASSIGN(FileRunnerDelegate);
};
diff --git a/gin/try_catch.cc b/gin/try_catch.cc
new file mode 100644
index 0000000..302d8bd
--- /dev/null
+++ b/gin/try_catch.cc
@@ -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.
+
+#include "gin/try_catch.h"
+
+#include "gin/converter.h"
+
+namespace gin {
+
+TryCatch::TryCatch() {
+}
+
+TryCatch::~TryCatch() {
+}
+
+bool TryCatch::HasCaught() {
+ return try_catch_.HasCaught();
+}
+
+std::string TryCatch::GetPrettyMessage() {
+ std::string info;
+ ConvertFromV8(try_catch_.Message()->Get(), &info);
+
+ std::string sounce_line;
+ if (ConvertFromV8(try_catch_.Message()->GetSourceLine(), &sounce_line))
+ info += "\n" + sounce_line;
+
+ return info;
+}
+
+} // namespace gin
diff --git a/gin/try_catch.h b/gin/try_catch.h
new file mode 100644
index 0000000..8026b3b
--- /dev/null
+++ b/gin/try_catch.h
@@ -0,0 +1,31 @@
+// 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_EXCEPTION_H_
+#define GIN_EXCEPTION_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+class TryCatch {
+ public:
+ TryCatch();
+ ~TryCatch();
+
+ bool HasCaught();
+ std::string GetPrettyMessage();
+
+ private:
+ v8::TryCatch try_catch_;
+
+ DISALLOW_COPY_AND_ASSIGN(TryCatch);
+};
+
+} // namespace gin
+
+#endif // GIN_EXCEPTION_H_