blob: 3e8b47597601d035919bbc419cef4a2628979df4 [file] [log] [blame]
// 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 <stddef.h>
#include <stdint.h>
#include <string>
#include <utility>
#include <vector>
#include "base/logging.h"
#include "gin/arguments.h"
#include "gin/converter.h"
#include "gin/modules/module_registry_observer.h"
#include "gin/per_context_data.h"
#include "gin/per_isolate_data.h"
#include "gin/public/wrapper_info.h"
#include "gin/runner.h"
using v8::Context;
using v8::External;
using v8::Function;
using v8::FunctionTemplate;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::ObjectTemplate;
using v8::Persistent;
using v8::StackTrace;
using v8::String;
using v8::Value;
namespace gin {
struct PendingModule {
PendingModule();
~PendingModule();
std::string id;
std::vector<std::string> dependencies;
Persistent<Value> factory;
};
PendingModule::PendingModule() {
}
PendingModule::~PendingModule() {
factory.Reset();
}
namespace {
// Key for base::SupportsUserData::Data.
const char kModuleRegistryKey[] = "ModuleRegistry";
struct ModuleRegistryData : public base::SupportsUserData::Data {
std::unique_ptr<ModuleRegistry> registry;
};
void Define(const v8::FunctionCallbackInfo<Value>& info) {
Arguments args(info);
if (!info.Length())
return args.ThrowTypeError("At least one argument is required.");
std::string id;
std::vector<std::string> dependencies;
v8::Local<Value> factory;
if (!args.PeekNext().IsEmpty() && args.PeekNext()->IsString())
args.GetNext(&id);
if (!args.PeekNext().IsEmpty() && args.PeekNext()->IsArray())
args.GetNext(&dependencies);
if (!args.GetNext(&factory))
return args.ThrowError();
std::unique_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(), std::move(pending));
}
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);
templ->RemovePrototype();
data->SetFunctionTemplate(&g_wrapper_info, templ);
}
return templ;
}
} // namespace
ModuleRegistry::ModuleRegistry(Isolate* isolate)
: modules_(isolate, Object::New(isolate)) {
}
ModuleRegistry::~ModuleRegistry() {
modules_.Reset();
}
// static
void ModuleRegistry::RegisterGlobals(Isolate* isolate,
v8::Local<ObjectTemplate> templ) {
templ->Set(StringToSymbol(isolate, "define"), GetDefineTemplate(isolate));
}
// static
bool ModuleRegistry::InstallGlobals(v8::Isolate* isolate,
v8::Local<v8::Object> obj) {
v8::Local<v8::Function> function;
auto maybe_function =
GetDefineTemplate(isolate)->GetFunction(isolate->GetCurrentContext());
if (!maybe_function.ToLocal(&function))
return false;
return SetProperty(isolate, obj, StringToSymbol(isolate, "define"), function);
}
// static
ModuleRegistry* ModuleRegistry::From(v8::Local<Context> context) {
PerContextData* data = PerContextData::From(context);
if (!data)
return NULL;
ModuleRegistryData* registry_data = static_cast<ModuleRegistryData*>(
data->GetUserData(kModuleRegistryKey));
if (!registry_data) {
// PerContextData takes ownership of ModuleRegistryData.
registry_data = new ModuleRegistryData;
registry_data->registry.reset(new ModuleRegistry(context->GetIsolate()));
data->SetUserData(kModuleRegistryKey, registry_data);
}
return registry_data->registry.get();
}
void ModuleRegistry::AddObserver(ModuleRegistryObserver* observer) {
observer_list_.AddObserver(observer);
}
void ModuleRegistry::RemoveObserver(ModuleRegistryObserver* observer) {
observer_list_.RemoveObserver(observer);
}
void ModuleRegistry::AddBuiltinModule(Isolate* isolate, const std::string& id,
v8::Local<Value> module) {
DCHECK(!id.empty());
RegisterModule(isolate, id, module);
}
void ModuleRegistry::AddPendingModule(Isolate* isolate,
std::unique_ptr<PendingModule> pending) {
const std::string pending_id = pending->id;
const std::vector<std::string> pending_dependencies = pending->dependencies;
AttemptToLoad(isolate, std::move(pending));
for (auto& observer : observer_list_)
observer.OnDidAddPendingModule(pending_id, pending_dependencies);
}
void ModuleRegistry::LoadModule(Isolate* isolate,
const std::string& id,
LoadModuleCallback callback) {
if (available_modules_.find(id) != available_modules_.end()) {
// Should we call the callback asynchronously?
callback.Run(GetModule(isolate, id));
return;
}
waiting_callbacks_.insert(std::make_pair(id, callback));
for (size_t i = 0; i < pending_modules_.size(); ++i) {
if (pending_modules_[i]->id == id)
return;
}
unsatisfied_dependencies_.insert(id);
}
bool ModuleRegistry::RegisterModule(Isolate* isolate,
const std::string& id,
v8::Local<Value> module) {
if (id.empty() || module.IsEmpty())
return false;
v8::Local<Object> modules = Local<Object>::New(isolate, modules_);
if (!SetProperty(isolate, modules, StringToSymbol(isolate, id), module))
return false;
unsatisfied_dependencies_.erase(id);
available_modules_.insert(id);
std::pair<LoadModuleCallbackMap::iterator, LoadModuleCallbackMap::iterator>
range = waiting_callbacks_.equal_range(id);
std::vector<LoadModuleCallback> callbacks;
callbacks.reserve(waiting_callbacks_.count(id));
for (LoadModuleCallbackMap::iterator it = range.first; it != range.second;
++it) {
callbacks.push_back(it->second);
}
waiting_callbacks_.erase(range.first, range.second);
for (std::vector<LoadModuleCallback>::iterator it = callbacks.begin();
it != callbacks.end();
++it) {
// Should we call the callback asynchronously?
it->Run(module);
}
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;
}
bool ModuleRegistry::Load(Isolate* isolate,
std::unique_ptr<PendingModule> pending) {
if (!pending->id.empty() && available_modules_.count(pending->id))
return true; // We've already loaded this module.
uint32_t argc = static_cast<uint32_t>(pending->dependencies.size());
std::vector<v8::Local<Value> > argv(argc);
for (uint32_t i = 0; i < argc; ++i)
argv[i] = GetModule(isolate, pending->dependencies[i]);
v8::Local<Value> module = Local<Value>::New(isolate, pending->factory);
v8::Local<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);
}
return RegisterModule(isolate, pending->id, module);
}
bool ModuleRegistry::AttemptToLoad(Isolate* isolate,
std::unique_ptr<PendingModule> pending) {
if (!CheckDependencies(pending.get())) {
pending_modules_.push_back(pending.release());
return false;
}
return Load(isolate, std::move(pending));
}
v8::Local<v8::Value> ModuleRegistry::GetModule(v8::Isolate* isolate,
const std::string& id) {
v8::Local<Object> modules = Local<Object>::New(isolate, modules_);
v8::Local<String> key = StringToSymbol(isolate, id);
DCHECK(modules->HasOwnProperty(isolate->GetCurrentContext(), key).FromJust());
return modules->Get(isolate->GetCurrentContext(), key).ToLocalChecked();
}
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) {
std::unique_ptr<PendingModule> pending(pending_modules[i]);
pending_modules[i] = NULL;
if (AttemptToLoad(isolate, std::move(pending)))
keep_trying = true;
}
}
}
} // namespace gin