// 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 #include #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 dependencies; Persistent factory; }; PendingModule::PendingModule() { } PendingModule::~PendingModule() { factory.Reset(); } namespace { void Define(const v8::FunctionCallbackInfo& info) { Arguments args(info); if (!info.Length()) return args.ThrowTypeError("At least one argument is required."); std::string id; std::vector dependencies; v8::Handle factory; if (args.PeekNext()->IsString()) args.GetNext(&id); if (args.PeekNext()->IsArray()) args.GetNext(&dependencies); if (!args.GetNext(&factory)) return args.ThrowError(); scoped_ptr 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 GetDefineTemplate(Isolate* isolate) { PerIsolateData* data = PerIsolateData::From(isolate); Local templ = data->GetFunctionTemplate( &g_wrapper_info); if (templ.IsEmpty()) { templ = FunctionTemplate::New(isolate, Define); data->SetFunctionTemplate(&g_wrapper_info, templ); } return templ; } v8::Handle 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 templ) { templ->Set(StringToSymbol(isolate, "define"), GetDefineTemplate(isolate)); } ModuleRegistry* ModuleRegistry::From(v8::Handle context) { Isolate* isolate = context->GetIsolate(); v8::Handle key = GetHiddenValueKey(isolate); v8::Handle value = context->Global()->GetHiddenValue(key); v8::Handle 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(registry)); return registry; } return static_cast(external->Value()); } void ModuleRegistry::AddBuiltinModule(Isolate* isolate, const std::string& id, v8::Handle templ) { DCHECK(!id.empty()); RegisterModule(isolate, id, templ->NewInstance()); } void ModuleRegistry::AddPendingModule(Isolate* isolate, scoped_ptr 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 module) { if (id.empty() || module.IsEmpty()) return; unsatisfied_dependencies_.erase(id); available_modules_.insert(id); v8::Handle modules = Local::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->Global()->SetHiddenValue(GetHiddenValueKey(context->GetIsolate()), v8::Handle()); } 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 pending) { if (!pending->id.empty() && available_modules_.count(pending->id)) return; // We've already loaded this module. uint32_t argc = static_cast(pending->dependencies.size()); std::vector > argv(argc); for (uint32_t i = 0; i < argc; ++i) argv[i] = GetModule(isolate, pending->dependencies[i]); v8::Handle module = Local::New(isolate, pending->factory); v8::Handle 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 pending) { if (!CheckDependencies(pending.get())) { pending_modules_.push_back(pending.release()); return false; } Load(isolate, pending.Pass()); return true; } v8::Handle ModuleRegistry::GetModule(v8::Isolate* isolate, const std::string& id) { v8::Handle modules = Local::New(isolate, modules_); v8::Handle 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 pending(pending_modules[i]); pending_modules[i] = NULL; if (AttemptToLoad(isolate, pending.Pass())) keep_trying = true; } } } } // namespace gin