summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorabarth@chromium.org <abarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-17 17:46:07 +0000
committerabarth@chromium.org <abarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-17 17:46:07 +0000
commit97f21cad04bb80834a8cc84bfc3dd24a96531a16 (patch)
tree4e49aa68b49f90e4e68c220cc9d9b04be95a6a5d
parent00509a3ac0c1c11e97852d2331d116df6754eb5d (diff)
downloadchromium_src-97f21cad04bb80834a8cc84bfc3dd24a96531a16.zip
chromium_src-97f21cad04bb80834a8cc84bfc3dd24a96531a16.tar.gz
chromium_src-97f21cad04bb80834a8cc84bfc3dd24a96531a16.tar.bz2
This CL implements the Asynchronous Module Definition (AMD)
API, which we plan to use for JavaScript in Mojo. We don't yet implement every feature in the AMD spec <https://github.com/amdjs/amdjs-api/wiki/AMD>, but we implement the basic framework, which will let us get started writing and testing JavaScript modules in Mojo. The two other leading choices for a modules system are CommonJS and ES6 modules. We decided not to use CommonJS, despite its popularity, because it implies the ability to load modules synchronously. That works well in server environments like node.js, but it won't work well for Mojo where modules might be loaded across a network. I would really like to have used ES6 modules, but the spec isn't finalized yet and V8 doesn't yet implement them. It's likely that we'll replace this AMD module system with ES6 modules once ES6 modules are ready. Structurally, I've implemented AMD in the ModuleRegistry class in a new "modules" directory in Gin. Nothing else in Gin (except the tests) depends on ModuleRegistry, which means folks are free to use Gin without AMD. At the Mojo layer, I've added a dependency on AMD. BUG=317398 Review URL: https://codereview.chromium.org/62333018 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@235543 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--gin/arguments.cc6
-rw-r--r--gin/arguments.h2
-rw-r--r--gin/context_holder.cc34
-rw-r--r--gin/context_holder.h36
-rw-r--r--gin/converter.cc25
-rw-r--r--gin/converter.h16
-rw-r--r--gin/gin.gyp9
-rw-r--r--gin/modules/module_registry.cc188
-rw-r--r--gin/modules/module_registry.h65
-rw-r--r--gin/modules/module_registry_unittests.js30
-rw-r--r--gin/per_context_data.cc49
-rw-r--r--gin/per_context_data.h48
-rw-r--r--gin/per_isolate_data.cc14
-rw-r--r--gin/per_isolate_data.h6
-rw-r--r--gin/runner.cc51
-rw-r--r--gin/runner.h27
-rw-r--r--gin/runner_unittest.cc28
-rw-r--r--gin/test/file_runner.cc64
-rw-r--r--gin/test/file_runner.h30
-rw-r--r--gin/test/gtest_unittests.js11
-rw-r--r--gin/test/run_js_tests.cc37
-rw-r--r--mojo/mojo.gyp6
-rw-r--r--mojo/public/bindings/js/core_unittests.js81
-rw-r--r--mojo/public/bindings/js/global.cc (renamed from mojo/public/bindings/js/mojo.cc)15
-rw-r--r--mojo/public/bindings/js/global.h (renamed from mojo/public/bindings/js/mojo.h)2
-rw-r--r--mojo/public/bindings/js/mojo_unittests.js8
-rw-r--r--mojo/public/bindings/js/runner_delegate.cc18
-rw-r--r--mojo/public/bindings/js/runner_delegate.h4
-rw-r--r--mojo/public/bindings/js/test/harness.cc79
-rw-r--r--mojo/public/bindings/js/test/run_js_tests.cc49
30 files changed, 827 insertions, 211 deletions
diff --git a/gin/arguments.cc b/gin/arguments.cc
index 15802b8..0a2375c 100644
--- a/gin/arguments.cc
+++ b/gin/arguments.cc
@@ -19,6 +19,12 @@ Arguments::Arguments(const v8::FunctionCallbackInfo<v8::Value>& info)
Arguments::~Arguments() {
}
+v8::Handle<v8::Value> Arguments::PeekNext() {
+ if (next_ >= info_.Length())
+ return v8::Handle<v8::Value>();
+ return info_[next_];
+}
+
void Arguments::ThrowError() {
if (insufficient_arguments_)
return ThrowTypeError("Insufficient number of arguments.");
diff --git a/gin/arguments.h b/gin/arguments.h
index c5335cc..1882640 100644
--- a/gin/arguments.h
+++ b/gin/arguments.h
@@ -35,6 +35,8 @@ class Arguments {
info_.GetReturnValue().Set(ConvertToV8(isolate_, val));
}
+ v8::Handle<v8::Value> PeekNext();
+
void ThrowError();
void ThrowTypeError(const std::string& message);
diff --git a/gin/context_holder.cc b/gin/context_holder.cc
new file mode 100644
index 0000000..077feb7
--- /dev/null
+++ b/gin/context_holder.cc
@@ -0,0 +1,34 @@
+// 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/context_holder.h"
+
+#include <assert.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();
+
+ PerContextData* data = PerContextData::From(context);
+ data->Detach(context);
+ delete data;
+
+ // TODO(abarth): Figure out how to set kResetInDestructor to true.
+ context_.Reset();
+}
+
+void ContextHolder::SetContext(v8::Handle<v8::Context> context) {
+ assert(context_.IsEmpty());
+ context_.Reset(isolate_, context);
+ new PerContextData(context); // Deleted in ~ContextHolder.
+}
+
+} // namespace gin
diff --git a/gin/context_holder.h b/gin/context_holder.h
new file mode 100644
index 0000000..c9068d1
--- /dev/null
+++ b/gin/context_holder.h
@@ -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.
+
+#ifndef GIN_CONTEXT_HOLDER_H_
+#define GIN_CONTEXT_HOLDER_H_
+
+#include <list>
+#include "base/basictypes.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+class 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_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContextHolder);
+};
+
+} // namespace gin
+
+#endif // GIN_CONTEXT_HOLDER_H_
diff --git a/gin/converter.cc b/gin/converter.cc
index 64f5954..5fda673 100644
--- a/gin/converter.cc
+++ b/gin/converter.cc
@@ -7,6 +7,7 @@
#include "v8/include/v8.h"
using v8::Boolean;
+using v8::External;
using v8::Function;
using v8::Handle;
using v8::Integer;
@@ -124,6 +125,30 @@ bool Converter<Handle<Object> >::FromV8(Handle<Value> val,
return true;
}
+Handle<Value> Converter<Handle<External> >::ToV8(v8::Isolate* isolate,
+ Handle<External> val) {
+ return val.As<Value>();
+}
+
+bool Converter<Handle<External> >::FromV8(Handle<Value> val,
+ Handle<External>* out) {
+ if (!val->IsExternal())
+ return false;
+ *out = Handle<External>::Cast(val);
+ return true;
+}
+
+Handle<Value> Converter<Handle<Value> >::ToV8(v8::Isolate* isolate,
+ Handle<Value> val) {
+ return val;
+}
+
+bool Converter<Handle<Value> >::FromV8(Handle<Value> val,
+ Handle<Value>* out) {
+ *out = val;
+ return true;
+}
+
v8::Handle<v8::String> StringToSymbol(v8::Isolate* isolate,
const std::string& val) {
return String::NewFromUtf8(isolate,
diff --git a/gin/converter.h b/gin/converter.h
index 551849b..33af452 100644
--- a/gin/converter.h
+++ b/gin/converter.h
@@ -87,6 +87,22 @@ struct Converter<v8::Handle<v8::Object> > {
v8::Handle<v8::Object>* out);
};
+template<>
+struct Converter<v8::Handle<v8::External> > {
+ static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+ v8::Handle<v8::External> val);
+ static bool FromV8(v8::Handle<v8::Value> val,
+ v8::Handle<v8::External>* out);
+};
+
+template<>
+struct Converter<v8::Handle<v8::Value> > {
+ static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val);
+ static bool FromV8(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,
diff --git a/gin/gin.gyp b/gin/gin.gyp
index ed76f09..597482a 100644
--- a/gin/gin.gyp
+++ b/gin/gin.gyp
@@ -20,16 +20,22 @@
'../v8/tools/gyp/v8.gyp:v8',
],
'sources': [
+ 'modules/module_registry.cc',
+ 'modules/module_registry.h',
'arguments.cc',
'arguments.h',
'array_buffer.cc',
'array_buffer.h',
'converter.cc',
'converter.h',
+ 'context_holder.cc',
+ 'context_holder.h',
'dictionary.cc',
'dictionary.h',
'initialize.cc',
'initialize.h',
+ 'per_context_data.cc',
+ 'per_context_data.h',
'per_isolate_data.cc',
'per_isolate_data.h',
'runner.cc',
@@ -52,6 +58,8 @@
'gin',
],
'sources': [
+ 'test/file_runner.cc',
+ 'test/file_runner.h',
'test/gtest.cc',
'test/gtest.h',
'test/v8_test.cc',
@@ -68,6 +76,7 @@
'sources': [
'converter_unittest.cc',
'test/run_all_unittests.cc',
+ 'test/run_js_tests.cc',
'runner_unittest.cc',
],
},
diff --git a/gin/modules/module_registry.cc b/gin/modules/module_registry.cc
new file mode 100644
index 0000000..78a102c
--- /dev/null
+++ b/gin/modules/module_registry.cc
@@ -0,0 +1,188 @@
+// 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 <assert.h>
+#include <string>
+#include <vector>
+#include "gin/arguments.h"
+#include "gin/converter.h"
+#include "gin/per_isolate_data.h"
+#include "gin/wrapper_info.h"
+
+using v8::External;
+using v8::Handle;
+using v8::Isolate;
+using v8::ObjectTemplate;
+
+namespace gin {
+
+struct PendingModule {
+ PendingModule();
+ ~PendingModule();
+
+ std::string id;
+ std::vector<std::string> dependencies;
+ v8::Persistent<v8::Value> factory;
+};
+
+namespace {
+
+void Define(const v8::FunctionCallbackInfo<v8::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;
+ Handle<v8::Value> factory;
+
+ if (args.PeekNext()->IsString())
+ args.GetNext(&id);
+ if (args.PeekNext()->IsArray())
+ args.GetNext(&dependencies);
+ if (!args.GetNext(&factory))
+ return args.ThrowError();
+
+ 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);
+}
+
+WrapperInfo g_wrapper_info = {};
+
+v8::Local<v8::FunctionTemplate> GetDefineTemplate(v8::Isolate* isolate) {
+ PerIsolateData* data = PerIsolateData::From(isolate);
+ v8::Local<v8::FunctionTemplate> templ = data->GetFunctionTemplate(
+ &g_wrapper_info);
+ if (templ.IsEmpty()) {
+ templ = v8::FunctionTemplate::New(Define);
+ data->SetFunctionTemplate(&g_wrapper_info, templ);
+ }
+ return templ;
+}
+
+Handle<v8::String> GetHiddenValueKey(v8::Isolate* isolate) {
+ return StringToSymbol(isolate, "::gin::ModuleRegistry");
+}
+
+} // namespace
+
+
+PendingModule::PendingModule() {
+}
+
+PendingModule::~PendingModule() {
+ factory.Reset();
+}
+
+ModuleRegistry::ModuleRegistry(v8::Isolate* isolate)
+ : modules_(isolate, v8::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) {
+ 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;
+ 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(registry));
+ data->AddSupplement(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::Detach(Handle<v8::Context> context) {
+ context->Global()->SetHiddenValue(GetHiddenValueKey(context->GetIsolate()),
+ Handle<v8::Value>());
+}
+
+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);
+
+ if (!pending->id.empty() && modules->HasOwnProperty(key)) {
+ // We've already loaded a module with this name. Ignore the new one.
+ delete pending;
+ return true;
+ }
+
+ size_t argc = pending->dependencies.size();
+ std::vector<Handle<v8::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;
+ }
+ argv[i] = modules->Get(key);
+ }
+
+ Handle<v8::Value> module = v8::Local<v8::Value>::New(
+ isolate, pending->factory);
+
+ Handle<v8::Function> factory;
+ if (ConvertFromV8(module, &factory)) {
+ v8::Handle<v8::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);
+
+ delete pending;
+ 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);
+ }
+}
+
+} // namespace gin
diff --git a/gin/modules/module_registry.h b/gin/modules/module_registry.h
new file mode 100644
index 0000000..7cff355
--- /dev/null
+++ b/gin/modules/module_registry.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_MODULES_MODULE_REGISTRY_H_
+#define GIN_MODULES_MODULE_REGISTRY_H_
+
+#include <list>
+#include <string>
+#include "base/compiler_specific.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 ModuleRegistry : public ContextSupplement {
+ public:
+ 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);
+
+ // Takes ownership of |pending|. The caller must have already entered
+ // our context.
+ void AddPendingModule(v8::Isolate* isolate, PendingModule* pending);
+
+ private:
+ typedef std::list<PendingModule*> PendingModuleList; // Owning reference.
+
+ 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);
+
+ v8::Persistent<v8::Object> modules_;
+ PendingModuleList pending_modules_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModuleRegistry);
+};
+
+} // namespace gin
+
+#endif // GIN_MODULES_MODULE_REGISTRY_H_
diff --git a/gin/modules/module_registry_unittests.js b/gin/modules/module_registry_unittests.js
new file mode 100644
index 0000000..ca70148
--- /dev/null
+++ b/gin/modules/module_registry_unittests.js
@@ -0,0 +1,30 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+define("module0", function() {
+ return {
+ "foo": "bar",
+ }
+});
+
+define("module2", [
+ "gtest",
+ "module0",
+ "module1"
+ ], function(gtest, module0, module1) {
+ gtest.expectEqual(module0.foo, "bar",
+ "module0.foo is " + module0.foo);
+ gtest.expectFalse(module0.bar,
+ "module0.bar is " + module0.bar);
+ gtest.expectEqual(module1.baz, "qux",
+ "module1.baz is " + module1.baz);
+ gtest.expectFalse(module1.qux,
+ "module1.qux is " + module1.qux);
+
+ this.result = "PASS";
+});
+
+define("module1", {
+ "baz": "qux",
+});
diff --git a/gin/per_context_data.cc b/gin/per_context_data.cc
new file mode 100644
index 0000000..8f6261a
--- /dev/null
+++ b/gin/per_context_data.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/per_context_data.h"
+
+#include <assert.h>
+#include "gin/wrapper_info.h"
+
+namespace gin {
+
+ContextSupplement::ContextSupplement() {
+}
+
+ContextSupplement::~ContextSupplement() {
+}
+
+PerContextData::PerContextData(v8::Handle<v8::Context> context) {
+ context->SetAlignedPointerInEmbedderData(kEncodedValueIndex, this);
+}
+
+PerContextData::~PerContextData() {
+ assert(supplements_.empty());
+}
+
+void PerContextData::Detach(v8::Handle<v8::Context> context) {
+ assert(From(context) == this);
+ context->SetAlignedPointerInEmbedderData(kEncodedValueIndex, NULL);
+
+ SuplementVector supplements;
+ supplements.swap(supplements_);
+
+ for (SuplementVector::iterator it = supplements.begin();
+ it != supplements.end(); ++it) {
+ (*it)->Detach(context);
+ delete *it;
+ }
+}
+
+PerContextData* PerContextData::From(v8::Handle<v8::Context> context) {
+ return static_cast<PerContextData*>(
+ context->GetAlignedPointerFromEmbedderData(kEncodedValueIndex));
+}
+
+void PerContextData::AddSupplement(ContextSupplement* supplement) {
+ supplements_.push_back(supplement);
+}
+
+} // namespace gin
diff --git a/gin/per_context_data.h b/gin/per_context_data.h
new file mode 100644
index 0000000..123db59
--- /dev/null
+++ b/gin/per_context_data.h
@@ -0,0 +1,48 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef GIN_PER_CONTEXT_DATA_H_
+#define GIN_PER_CONTEXT_DATA_H_
+
+#include <vector>
+#include "base/basictypes.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+class ContextSupplement {
+ public:
+ ContextSupplement();
+ virtual ~ContextSupplement();
+
+ virtual void Detach(v8::Handle<v8::Context> context) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ContextSupplement);
+};
+
+class 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);
+
+ // Takes ownership of the supplement.
+ void AddSupplement(ContextSupplement* supplement);
+
+ private:
+ typedef std::vector<ContextSupplement*> SuplementVector;
+
+ // Owning reference.
+ SuplementVector supplements_;
+
+ DISALLOW_COPY_AND_ASSIGN(PerContextData);
+};
+
+} // namespace gin
+
+#endif // GIN_PER_CONTEXT_DATA_H_
diff --git a/gin/per_isolate_data.cc b/gin/per_isolate_data.cc
index 56b2c53..b18a89a 100644
--- a/gin/per_isolate_data.cc
+++ b/gin/per_isolate_data.cc
@@ -9,6 +9,7 @@ using v8::Handle;
using v8::Isolate;
using v8::Local;
using v8::Object;
+using v8::FunctionTemplate;
using v8::ObjectTemplate;
namespace gin {
@@ -30,6 +31,11 @@ void PerIsolateData::SetObjectTemplate(WrapperInfo* info,
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);
@@ -38,4 +44,12 @@ v8::Local<v8::ObjectTemplate> PerIsolateData::GetObjectTemplate(
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/gin/per_isolate_data.h b/gin/per_isolate_data.h
index c92e9f3..e758744 100644
--- a/gin/per_isolate_data.h
+++ b/gin/per_isolate_data.h
@@ -22,15 +22,21 @@ class PerIsolateData {
void SetObjectTemplate(WrapperInfo* info,
v8::Local<v8::ObjectTemplate> object_template);
+ void SetFunctionTemplate(WrapperInfo* info,
+ v8::Local<v8::FunctionTemplate> function_template);
v8::Local<v8::ObjectTemplate> GetObjectTemplate(WrapperInfo* info);
+ v8::Local<v8::FunctionTemplate> GetFunctionTemplate(WrapperInfo* info);
private:
typedef std::map<
WrapperInfo*, v8::Eternal<v8::ObjectTemplate> > ObjectTemplateMap;
+ typedef std::map<
+ WrapperInfo*, v8::Eternal<v8::FunctionTemplate> > FunctionTemplateMap;
v8::Isolate* isolate_;
ObjectTemplateMap object_templates_;
+ FunctionTemplateMap function_templates_;
DISALLOW_COPY_AND_ASSIGN(PerIsolateData);
};
diff --git a/gin/runner.cc b/gin/runner.cc
index ec701b7..4b0b6f9 100644
--- a/gin/runner.cc
+++ b/gin/runner.cc
@@ -7,55 +7,52 @@
#include "gin/converter.h"
using v8::Context;
-using v8::Function;
using v8::Handle;
using v8::HandleScope;
using v8::Isolate;
-using v8::Local;
using v8::Object;
+using v8::ObjectTemplate;
using v8::Script;
-using v8::String;
-using v8::Value;
namespace gin {
+RunnerDelegate::RunnerDelegate() {
+}
+
RunnerDelegate::~RunnerDelegate() {
}
+Handle<ObjectTemplate> RunnerDelegate::GetGlobalTemplate(Runner* runner) {
+ return Handle<ObjectTemplate>();
+}
+
+void RunnerDelegate::DidCreateContext(Runner* runner) {
+}
+
Runner::Runner(RunnerDelegate* delegate, Isolate* isolate)
- : delegate_(delegate),
- isolate_(isolate) {
- HandleScope handle_scope(isolate_);
- context_.Reset(isolate_, Context::New(isolate_));
+ : ContextHolder(isolate),
+ delegate_(delegate) {
+ HandleScope handle_scope(isolate);
+ SetContext(Context::New(isolate, NULL, delegate_->GetGlobalTemplate(this)));
+
+ v8::Context::Scope scope(context());
+ delegate_->DidCreateContext(this);
}
Runner::~Runner() {
- // TODO(abarth): Figure out how to set kResetInDestructor to true.
- context_.Reset();
}
-void Runner::Run(Handle<Script> script) {
- script->Run();
- Handle<Function> main = GetMain();
- if (main.IsEmpty())
- return;
- Handle<Value> argv[] = { delegate_->CreateRootObject(this) };
- main->Call(global(), 1, argv);
+void Runner::Run(const std::string& script) {
+ Run(Script::New(StringToV8(isolate(), script)));
}
-v8::Handle<v8::Function> Runner::GetMain() {
- Handle<Value> property = global()->Get(StringToSymbol(isolate_, "main"));
- if (property.IsEmpty())
- return v8::Handle<v8::Function>();
- Handle<Function> main;
- if (!ConvertFromV8(property, &main))
- return v8::Handle<v8::Function>();
- return main;
+void Runner::Run(Handle<Script> script) {
+ script->Run();
}
Runner::Scope::Scope(Runner* runner)
- : handle_scope_(runner->isolate_),
- scope_(Local<Context>::New(runner->isolate_, runner->context_)) {
+ : handle_scope_(runner->isolate()),
+ scope_(runner->context()) {
}
Runner::Scope::~Scope() {
diff --git a/gin/runner.h b/gin/runner.h
index c74de16..3e999e1f 100644
--- a/gin/runner.h
+++ b/gin/runner.h
@@ -5,8 +5,8 @@
#ifndef GIN_RUNNER_H_
#define GIN_RUNNER_H_
-#include "base/basictypes.h"
-#include "v8/include/v8.h"
+#include <string>
+#include "gin/context_holder.h"
namespace gin {
@@ -14,26 +14,23 @@ class Runner;
class RunnerDelegate {
public:
- // Returns the object that is passed to the script's |main| function.
- virtual v8::Handle<v8::Object> CreateRootObject(Runner* runner) = 0;
-
- protected:
+ RunnerDelegate();
virtual ~RunnerDelegate();
+
+ // Returns the template for the global object.
+ virtual v8::Handle<v8::ObjectTemplate> GetGlobalTemplate(Runner* runner);
+
+ virtual void DidCreateContext(Runner* runner);
};
-class Runner {
+class Runner : public ContextHolder {
public:
Runner(RunnerDelegate* delegate, v8::Isolate* isolate);
~Runner();
+ void Run(const std::string& script);
void Run(v8::Handle<v8::Script> script);
- v8::Isolate* isolate() const { return isolate_; }
-
- v8::Handle<v8::Context> context() const {
- return v8::Local<v8::Context>::New(isolate_, context_);
- }
-
v8::Handle<v8::Object> global() const {
return context()->Global();
}
@@ -53,11 +50,7 @@ class Runner {
private:
friend class Scope;
- v8::Handle<v8::Function> GetMain();
-
RunnerDelegate* delegate_;
- v8::Isolate* isolate_;
- v8::Persistent<v8::Context> context_;
DISALLOW_COPY_AND_ASSIGN(Runner);
};
diff --git a/gin/runner_unittest.cc b/gin/runner_unittest.cc
index 4b3c0fc..a95a113 100644
--- a/gin/runner_unittest.cc
+++ b/gin/runner_unittest.cc
@@ -15,35 +15,15 @@ using v8::Script;
using v8::String;
namespace gin {
-namespace {
-
-class TestRunnerDelegate : public RunnerDelegate {
- public:
- virtual Handle<Object> CreateRootObject(Runner* runner) OVERRIDE {
- Isolate* isolate = runner->isolate();
- Handle<Object> root = Object::New();
- root->Set(StringToV8(isolate, "foo"), StringToV8(isolate, "bar"));
- return root;
- }
- virtual ~TestRunnerDelegate() {}
-};
-
-}
TEST(RunnerTest, Run) {
- std::string source =
- "function main(root) {\n"
- " if (root.foo)\n"
- " this.result = 'PASS';\n"
- " else\n"
- " this.result = 'FAIL';\n"
- "}\n";
-
- TestRunnerDelegate delegate;
+ std::string source = "this.result = 'PASS';\n";
+
+ RunnerDelegate delegate;
Isolate* isolate = Isolate::GetCurrent();
Runner runner(&delegate, isolate);
Runner::Scope scope(&runner);
- runner.Run(Script::New(StringToV8(isolate, source)));
+ runner.Run(source);
std::string result;
EXPECT_TRUE(Converter<std::string>::FromV8(
diff --git a/gin/test/file_runner.cc b/gin/test/file_runner.cc
new file mode 100644
index 0000000..a8d0cc2
--- /dev/null
+++ b/gin/test/file_runner.cc
@@ -0,0 +1,64 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gin/test/file_runner.h"
+
+#include "base/file_util.h"
+#include "gin/converter.h"
+#include "gin/modules/module_registry.h"
+#include "gin/test/gtest.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;
+}
+
+} // namespace
+
+FileRunnerDelegate::FileRunnerDelegate() {
+}
+
+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) {
+ v8::Handle<v8::Context> context = runner->context();
+ ModuleRegistry* registry = ModuleRegistry::From(context);
+
+ registry->AddBuiltinModule(runner->isolate(), "gtest",
+ GetGTestTemplate(runner->isolate()));
+}
+
+void RunTestFromFile(const base::FilePath& path, RunnerDelegate* delegate) {
+ ASSERT_TRUE(base::PathExists(path)) << path.LossyDisplayName();
+ std::string source;
+ ASSERT_TRUE(ReadFileToString(path, &source));
+ 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);
+
+ v8::Handle<v8::Value> result = runner.context()->Global()->Get(
+ StringToSymbol(runner.isolate(), "result"));
+ std::string result_string;
+ ASSERT_TRUE(ConvertFromV8(result, &result_string));
+ EXPECT_EQ("PASS", result_string);
+}
+
+} // namespace gin
diff --git a/gin/test/file_runner.h b/gin/test/file_runner.h
new file mode 100644
index 0000000..b4ca00e
--- /dev/null
+++ b/gin/test/file_runner.h
@@ -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.
+
+#ifndef GIN_TEST_FILE_RUNNER_H_
+#define GIN_TEST_FILE_RUNNER_H_
+
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "gin/runner.h"
+
+namespace gin {
+
+class FileRunnerDelegate : public RunnerDelegate {
+ public:
+ FileRunnerDelegate();
+ virtual ~FileRunnerDelegate();
+ virtual v8::Handle<v8::ObjectTemplate> GetGlobalTemplate(
+ Runner* runner) OVERRIDE;
+ virtual void DidCreateContext(Runner* runner) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FileRunnerDelegate);
+};
+
+void RunTestFromFile(const base::FilePath& path, RunnerDelegate* delegate);
+
+} // namespace gin
+
+#endif // GIN_TEST_FILE_RUNNER_H_
diff --git a/gin/test/gtest_unittests.js b/gin/test/gtest_unittests.js
new file mode 100644
index 0000000..1d566d5
--- /dev/null
+++ b/gin/test/gtest_unittests.js
@@ -0,0 +1,11 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+define(["gtest"], function(gtest) {
+ gtest.expectTrue(true, "true is true");
+ gtest.expectFalse(false, "false is false");
+ gtest.expectTrue(this, "this is " + this);
+
+ this.result = "PASS";
+});
diff --git a/gin/test/run_js_tests.cc b/gin/test/run_js_tests.cc
new file mode 100644
index 0000000..0b7c41a
--- /dev/null
+++ b/gin/test/run_js_tests.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 "base/file_util.h"
+#include "base/path_service.h"
+#include "gin/test/file_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gin {
+namespace {
+
+base::FilePath BasePath() {
+ base::FilePath path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ return path.AppendASCII("gin");
+}
+
+void RunTest(const base::FilePath& path) {
+ FileRunnerDelegate delegate;
+ RunTestFromFile(path, &delegate);
+}
+
+TEST(JSTest, GTest) {
+ RunTest(BasePath()
+ .AppendASCII("test")
+ .AppendASCII("gtest_unittests.js"));
+}
+
+TEST(JSTest, ModuleRegistry) {
+ RunTest(BasePath()
+ .AppendASCII("modules")
+ .AppendASCII("module_registry_unittests.js"));
+}
+
+} // namespace
+} // gin
diff --git a/mojo/mojo.gyp b/mojo/mojo.gyp
index 96517d1..c145ea5 100644
--- a/mojo/mojo.gyp
+++ b/mojo/mojo.gyp
@@ -436,10 +436,10 @@
'sources': [
'public/bindings/js/core.cc',
'public/bindings/js/core.h',
+ 'public/bindings/js/global.cc',
+ 'public/bindings/js/global.h',
'public/bindings/js/handle.cc',
'public/bindings/js/handle.h',
- 'public/bindings/js/mojo.cc',
- 'public/bindings/js/mojo.h',
'public/bindings/js/runner_delegate.cc',
'public/bindings/js/runner_delegate.h',
],
@@ -454,7 +454,7 @@
],
'sources': [
'public/bindings/js/test/run_all_unittests.cc',
- 'public/bindings/js/test/harness.cc',
+ 'public/bindings/js/test/run_js_tests.cc',
],
},
{
diff --git a/mojo/public/bindings/js/core_unittests.js b/mojo/public/bindings/js/core_unittests.js
index 2c08548..6f4e30c 100644
--- a/mojo/public/bindings/js/core_unittests.js
+++ b/mojo/public/bindings/js/core_unittests.js
@@ -2,56 +2,57 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-function runWithPipe(mojo, test) {
- var pipe = mojo.core.createMessagePipe();
+define(["gtest", "core"], function(gtest, core) {
+ runWithPipe(testNop);
+ runWithPipe(testReadAndWriteMessage);
+ this.result = "PASS";
- test(mojo, pipe);
+ function runWithPipe(test) {
+ var pipe = core.createMessagePipe();
- var result0 = mojo.core.close(pipe.handle0);
- mojo.gtest.expectEqual(result0, mojo.core.RESULT_OK,
- "result0 is " + result0);
+ test(pipe);
- var result1 = mojo.core.close(pipe.handle1);
- mojo.gtest.expectEqual(result1, mojo.core.RESULT_OK,
- "result1 is " + result1);
-}
+ var result0 = core.close(pipe.handle0);
+ gtest.expectEqual(result0, core.RESULT_OK,
+ "result0 is " + result0);
-function testNop(mojo, pipe) {
-}
+ var result1 = core.close(pipe.handle1);
+ gtest.expectEqual(result1, core.RESULT_OK,
+ "result1 is " + result1);
+ }
-function testReadAndWriteMessage(mojo, pipe) {
- var senderData = new Uint8Array(42);
- for (var i = 0; i < senderData.length; ++i) {
- senderData[i] = i * i;
+ function testNop(pipe) {
}
- var result = mojo.core.writeMessage(
- pipe.handle0, senderData, [],
- mojo.core.WRITE_MESSAGE_FLAG_NONE);
+ function testReadAndWriteMessage(pipe) {
+ var senderData = new Uint8Array(42);
+ for (var i = 0; i < senderData.length; ++i) {
+ senderData[i] = i * i;
+ }
- mojo.gtest.expectEqual(result, mojo.core.RESULT_OK,
- "writeMessage returned RESULT_OK: " + result);
+ var result = core.writeMessage(
+ pipe.handle0, senderData, [],
+ core.WRITE_MESSAGE_FLAG_NONE);
- var receiverData = new Uint8Array(50);
+ gtest.expectEqual(result, core.RESULT_OK,
+ "writeMessage returned RESULT_OK: " + result);
- var mesage = mojo.core.readMessage(
- pipe.handle1, receiverData, 10,
- mojo.core.READ_MESSAGE_FLAG_NONE)
+ var receiverData = new Uint8Array(50);
- mojo.gtest.expectEqual(mesage.result, mojo.core.RESULT_OK,
- "mesage.result is " + mesage.result);
- mojo.gtest.expectEqual(mesage.bytesRead, 42,
- "mesage.bytesRead is " + mesage.bytesRead);
- mojo.gtest.expectEqual(mesage.handles.length, 0,
- "mesage.handles.length is " + mesage.handles.length);
+ var mesage = core.readMessage(
+ pipe.handle1, receiverData, 10,
+ core.READ_MESSAGE_FLAG_NONE)
- for (var i = 0; i < mesage.bytesRead; ++i) {
- mojo.gtest.expectEqual(receiverData[i], (i * i) & 0xFF,
- "receiverData[" + i + "] is " + receiverData[i]);
- }
-}
+ gtest.expectEqual(mesage.result, core.RESULT_OK,
+ "mesage.result is " + mesage.result);
+ gtest.expectEqual(mesage.bytesRead, 42,
+ "mesage.bytesRead is " + mesage.bytesRead);
+ gtest.expectEqual(mesage.handles.length, 0,
+ "mesage.handles.length is " + mesage.handles.length);
-function main(mojo) {
- runWithPipe(mojo, testNop);
- runWithPipe(mojo, testReadAndWriteMessage);
-}
+ for (var i = 0; i < mesage.bytesRead; ++i) {
+ gtest.expectEqual(receiverData[i], (i * i) & 0xFF,
+ "receiverData[" + i + "] is " + receiverData[i]);
+ }
+ }
+});
diff --git a/mojo/public/bindings/js/mojo.cc b/mojo/public/bindings/js/global.cc
index a380bb6..e0187bf 100644
--- a/mojo/public/bindings/js/mojo.cc
+++ b/mojo/public/bindings/js/global.cc
@@ -2,30 +2,29 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "mojo/public/bindings/js/mojo.h"
+#include "mojo/public/bindings/js/global.h"
-#include "gin/converter.h"
+#include "gin/modules/module_registry.h"
#include "gin/per_isolate_data.h"
#include "gin/wrapper_info.h"
-#include "mojo/public/bindings/js/core.h"
namespace mojo {
namespace js {
namespace {
-gin::WrapperInfo g_mojo_wrapper_info = {};
+gin::WrapperInfo g_wrapper_info = {};
}
-v8::Local<v8::ObjectTemplate> GetMojoTemplate(v8::Isolate* isolate) {
+v8::Local<v8::ObjectTemplate> GetGlobalTemplate(v8::Isolate* isolate) {
gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate(
- &g_mojo_wrapper_info);
+ &g_wrapper_info);
if (templ.IsEmpty()) {
templ = v8::ObjectTemplate::New();
- templ->Set(gin::StringToSymbol(isolate, "core"), GetCoreTemplate(isolate));
- data->SetObjectTemplate(&g_mojo_wrapper_info, templ);
+ gin::ModuleRegistry::RegisterGlobals(isolate, templ);
+ data->SetObjectTemplate(&g_wrapper_info, templ);
}
return templ;
}
diff --git a/mojo/public/bindings/js/mojo.h b/mojo/public/bindings/js/global.h
index 95d6e13..d9ca889 100644
--- a/mojo/public/bindings/js/mojo.h
+++ b/mojo/public/bindings/js/global.h
@@ -10,7 +10,7 @@
namespace mojo {
namespace js {
-v8::Local<v8::ObjectTemplate> GetMojoTemplate(v8::Isolate* isolate);
+v8::Local<v8::ObjectTemplate> GetGlobalTemplate(v8::Isolate* isolate);
} // namespace js
} // namespace mojo
diff --git a/mojo/public/bindings/js/mojo_unittests.js b/mojo/public/bindings/js/mojo_unittests.js
deleted file mode 100644
index 1e24ed8..0000000
--- a/mojo/public/bindings/js/mojo_unittests.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// 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.
-
-function main(mojo) {
- mojo.gtest.expectTrue(mojo.core, "mojo.core");
- mojo.gtest.expectFalse(mojo.foo, "mojo.foo");
-}
diff --git a/mojo/public/bindings/js/runner_delegate.cc b/mojo/public/bindings/js/runner_delegate.cc
index f990caa..79203cb 100644
--- a/mojo/public/bindings/js/runner_delegate.cc
+++ b/mojo/public/bindings/js/runner_delegate.cc
@@ -4,7 +4,9 @@
#include "mojo/public/bindings/js/runner_delegate.h"
-#include "mojo/public/bindings/js/mojo.h"
+#include "gin/modules/module_registry.h"
+#include "mojo/public/bindings/js/core.h"
+#include "mojo/public/bindings/js/global.h"
namespace mojo {
namespace js {
@@ -15,8 +17,18 @@ RunnerDelegate::RunnerDelegate() {
RunnerDelegate::~RunnerDelegate() {
}
-v8::Handle<v8::Object> RunnerDelegate::CreateRootObject(gin::Runner* runner) {
- return GetMojoTemplate(runner->isolate())->NewInstance();
+v8::Handle<v8::ObjectTemplate> RunnerDelegate::GetGlobalTemplate(
+ gin::Runner* runner) {
+ return js::GetGlobalTemplate(runner->isolate());
+}
+
+void RunnerDelegate::DidCreateContext(gin::Runner* runner) {
+ v8::Handle<v8::Context> context = runner->context();
+ gin::ModuleRegistry* registry =
+ gin::ModuleRegistry::From(context);
+
+ registry->AddBuiltinModule(runner->isolate(), "core",
+ GetCoreTemplate(runner->isolate()));
}
} // namespace js
diff --git a/mojo/public/bindings/js/runner_delegate.h b/mojo/public/bindings/js/runner_delegate.h
index 8bacf44..bd5b880 100644
--- a/mojo/public/bindings/js/runner_delegate.h
+++ b/mojo/public/bindings/js/runner_delegate.h
@@ -16,9 +16,11 @@ class RunnerDelegate : public gin::RunnerDelegate {
RunnerDelegate();
virtual ~RunnerDelegate();
- virtual v8::Handle<v8::Object> CreateRootObject(
+ virtual v8::Handle<v8::ObjectTemplate> GetGlobalTemplate(
gin::Runner* runner) MOJO_OVERRIDE;
+ virtual void DidCreateContext(gin::Runner* runner) MOJO_OVERRIDE;
+
private:
MOJO_DISALLOW_COPY_AND_ASSIGN(RunnerDelegate);
};
diff --git a/mojo/public/bindings/js/test/harness.cc b/mojo/public/bindings/js/test/harness.cc
deleted file mode 100644
index 5b7319d..0000000
--- a/mojo/public/bindings/js/test/harness.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-// 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/file_util.h"
-#include "base/path_service.h"
-#include "gin/converter.h"
-#include "gin/runner.h"
-#include "gin/test/gtest.h"
-#include "mojo/public/bindings/js/runner_delegate.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using v8::Isolate;
-using v8::Object;
-using v8::Script;
-using v8::Value;
-
-namespace mojo {
-namespace js {
-namespace {
-
-class TestRunnerDelegate : public RunnerDelegate {
- public:
- virtual ~TestRunnerDelegate() {}
-
- virtual v8::Handle<Object> CreateRootObject(
- gin::Runner* runner) MOJO_OVERRIDE {
- v8::Handle<Object> root = RunnerDelegate::CreateRootObject(runner);
- root->Set(gin::StringToSymbol(runner->isolate(), "gtest"),
- gin::GetGTestTemplate(runner->isolate())->NewInstance());
- return root;
- }
-};
-
-std::string GetExceptionInfo(const v8::TryCatch& try_catch) {
- std::string info;
- gin::ConvertFromV8(try_catch.Message()->Get(), &info);
- return info;
-}
-
-void RunTestFromFile(const base::FilePath& path) {
- EXPECT_TRUE(base::PathExists(path)) << path.LossyDisplayName();
- std::string source;
- EXPECT_TRUE(ReadFileToString(path, &source));
- Isolate* isolate = Isolate::GetCurrent();
-
- TestRunnerDelegate delegate;
- gin::Runner runner(&delegate, isolate);
- gin::Runner::Scope scope(&runner);
-
- v8::TryCatch try_catch;
- runner.Run(Script::New(gin::StringToV8(isolate, source)));
-
- EXPECT_FALSE(try_catch.HasCaught()) << GetExceptionInfo(try_catch);
-}
-
-void RunTest(std::string test) {
- base::FilePath path;
- PathService::Get(base::DIR_SOURCE_ROOT, &path);
- path = path.AppendASCII("mojo")
- .AppendASCII("public")
- .AppendASCII("bindings")
- .AppendASCII("js")
- .AppendASCII(test);
- RunTestFromFile(path);
-}
-
-// TODO(abarth): Should we autogenerate these stubs from GYP?
-TEST(Harness, mojo_unittests_js) {
- RunTest("mojo_unittests.js");
-}
-
-TEST(Harness, core_unittests_js) {
- RunTest("core_unittests.js");
-}
-
-} // namespace
-} // namespace js
-} // namespace mojo
diff --git a/mojo/public/bindings/js/test/run_js_tests.cc b/mojo/public/bindings/js/test/run_js_tests.cc
new file mode 100644
index 0000000..b254cd6
--- /dev/null
+++ b/mojo/public/bindings/js/test/run_js_tests.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 "base/file_util.h"
+#include "base/path_service.h"
+#include "gin/modules/module_registry.h"
+#include "gin/test/file_runner.h"
+#include "gin/test/gtest.h"
+#include "mojo/public/bindings/js/runner_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace js {
+namespace {
+
+class TestRunnerDelegate : public RunnerDelegate {
+ virtual void DidCreateContext(gin::Runner* runner) MOJO_OVERRIDE {
+ RunnerDelegate::DidCreateContext(runner);
+
+ v8::Handle<v8::Context> context = runner->context();
+ gin::ModuleRegistry* registry =
+ gin::ModuleRegistry::From(context);
+
+ registry->AddBuiltinModule(runner->isolate(), "gtest",
+ gin::GetGTestTemplate(runner->isolate()));
+ }
+};
+
+void RunTest(std::string test) {
+ base::FilePath path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ path = path.AppendASCII("mojo")
+ .AppendASCII("public")
+ .AppendASCII("bindings")
+ .AppendASCII("js")
+ .AppendASCII(test);
+ TestRunnerDelegate delegate;
+ gin::RunTestFromFile(path, &delegate);
+}
+
+// TODO(abarth): Should we autogenerate these stubs from GYP?
+TEST(JSTest, core) {
+ RunTest("core_unittests.js");
+}
+
+} // namespace
+} // namespace js
+} // namespace mojo