Home | History | Annotate | Download | only in modules
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "gin/modules/module_registry.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/logging.h"
     11 #include "gin/arguments.h"
     12 #include "gin/converter.h"
     13 #include "gin/per_isolate_data.h"
     14 #include "gin/public/wrapper_info.h"
     15 #include "gin/runner.h"
     16 
     17 using v8::Context;
     18 using v8::External;
     19 using v8::Function;
     20 using v8::FunctionTemplate;
     21 using v8::Isolate;
     22 using v8::Local;
     23 using v8::Object;
     24 using v8::ObjectTemplate;
     25 using v8::Persistent;
     26 using v8::StackTrace;
     27 using v8::String;
     28 using v8::Value;
     29 
     30 namespace gin {
     31 
     32 struct PendingModule {
     33   PendingModule();
     34   ~PendingModule();
     35 
     36   std::string id;
     37   std::vector<std::string> dependencies;
     38   Persistent<Value> factory;
     39 };
     40 
     41 PendingModule::PendingModule() {
     42 }
     43 
     44 PendingModule::~PendingModule() {
     45   factory.Reset();
     46 }
     47 
     48 namespace {
     49 
     50 void Define(const v8::FunctionCallbackInfo<Value>& info) {
     51   Arguments args(info);
     52 
     53   if (!info.Length())
     54     return args.ThrowTypeError("At least one argument is required.");
     55 
     56   std::string id;
     57   std::vector<std::string> dependencies;
     58   v8::Handle<Value> factory;
     59 
     60   if (args.PeekNext()->IsString())
     61     args.GetNext(&id);
     62   if (args.PeekNext()->IsArray())
     63     args.GetNext(&dependencies);
     64   if (!args.GetNext(&factory))
     65     return args.ThrowError();
     66 
     67   scoped_ptr<PendingModule> pending(new PendingModule);
     68   pending->id = id;
     69   pending->dependencies = dependencies;
     70   pending->factory.Reset(args.isolate(), factory);
     71 
     72   ModuleRegistry* registry =
     73       ModuleRegistry::From(args.isolate()->GetCurrentContext());
     74   registry->AddPendingModule(args.isolate(), pending.Pass());
     75 }
     76 
     77 WrapperInfo g_wrapper_info = { kEmbedderNativeGin };
     78 
     79 Local<FunctionTemplate> GetDefineTemplate(Isolate* isolate) {
     80   PerIsolateData* data = PerIsolateData::From(isolate);
     81   Local<FunctionTemplate> templ = data->GetFunctionTemplate(
     82       &g_wrapper_info);
     83   if (templ.IsEmpty()) {
     84     templ = FunctionTemplate::New(isolate, Define);
     85     data->SetFunctionTemplate(&g_wrapper_info, templ);
     86   }
     87   return templ;
     88 }
     89 
     90 v8::Handle<String> GetHiddenValueKey(Isolate* isolate) {
     91   return StringToSymbol(isolate, "::gin::ModuleRegistry");
     92 }
     93 
     94 }  // namespace
     95 
     96 ModuleRegistry::ModuleRegistry(Isolate* isolate)
     97     : modules_(isolate, Object::New()) {
     98 }
     99 
    100 ModuleRegistry::~ModuleRegistry() {
    101   modules_.Reset();
    102 }
    103 
    104 void ModuleRegistry::RegisterGlobals(Isolate* isolate,
    105                                      v8::Handle<ObjectTemplate> templ) {
    106   templ->Set(StringToSymbol(isolate, "define"), GetDefineTemplate(isolate));
    107 }
    108 
    109 ModuleRegistry* ModuleRegistry::From(v8::Handle<Context> context) {
    110   Isolate* isolate = context->GetIsolate();
    111   v8::Handle<String> key = GetHiddenValueKey(isolate);
    112   v8::Handle<Value> value = context->Global()->GetHiddenValue(key);
    113   v8::Handle<External> external;
    114   if (value.IsEmpty() || !ConvertFromV8(isolate, value, &external)) {
    115     PerContextData* data = PerContextData::From(context);
    116     if (!data)
    117       return NULL;
    118     ModuleRegistry* registry = new ModuleRegistry(isolate);
    119     context->Global()->SetHiddenValue(key, External::New(isolate, registry));
    120     data->AddSupplement(scoped_ptr<ContextSupplement>(registry));
    121     return registry;
    122   }
    123   return static_cast<ModuleRegistry*>(external->Value());
    124 }
    125 
    126 void ModuleRegistry::AddBuiltinModule(Isolate* isolate,
    127                                       const std::string& id,
    128                                       v8::Handle<ObjectTemplate> templ) {
    129   DCHECK(!id.empty());
    130   RegisterModule(isolate, id, templ->NewInstance());
    131 }
    132 
    133 void ModuleRegistry::AddPendingModule(Isolate* isolate,
    134                                       scoped_ptr<PendingModule> pending) {
    135   AttemptToLoad(isolate, pending.Pass());
    136 }
    137 
    138 void ModuleRegistry::LoadModule(Isolate* isolate,
    139                                 const std::string& id,
    140                                 LoadModuleCallback callback) {
    141   if (available_modules_.find(id) != available_modules_.end()) {
    142     // Should we call the callback asynchronously?
    143     callback.Run(GetModule(isolate, id));
    144     return;
    145   }
    146   // Should we support multiple callers waiting on the same module?
    147   DCHECK(waiting_callbacks_.find(id) == waiting_callbacks_.end());
    148   waiting_callbacks_[id] = callback;
    149   unsatisfied_dependencies_.insert(id);
    150 }
    151 
    152 void ModuleRegistry::RegisterModule(Isolate* isolate,
    153                                     const std::string& id,
    154                                     v8::Handle<Value> module) {
    155   if (id.empty() || module.IsEmpty())
    156     return;
    157 
    158   unsatisfied_dependencies_.erase(id);
    159   available_modules_.insert(id);
    160   v8::Handle<Object> modules = Local<Object>::New(isolate, modules_);
    161   modules->Set(StringToSymbol(isolate, id), module);
    162 
    163   LoadModuleCallbackMap::iterator it = waiting_callbacks_.find(id);
    164   if (it == waiting_callbacks_.end())
    165     return;
    166   LoadModuleCallback callback = it->second;
    167   waiting_callbacks_.erase(it);
    168   // Should we call the callback asynchronously?
    169   callback.Run(module);
    170 }
    171 
    172 void ModuleRegistry::Detach(v8::Handle<Context> context) {
    173   context->Global()->SetHiddenValue(GetHiddenValueKey(context->GetIsolate()),
    174                                     v8::Handle<Value>());
    175 }
    176 
    177 bool ModuleRegistry::CheckDependencies(PendingModule* pending) {
    178   size_t num_missing_dependencies = 0;
    179   size_t len = pending->dependencies.size();
    180   for (size_t i = 0; i < len; ++i) {
    181     const std::string& dependency = pending->dependencies[i];
    182     if (available_modules_.count(dependency))
    183       continue;
    184     unsatisfied_dependencies_.insert(dependency);
    185     num_missing_dependencies++;
    186   }
    187   return num_missing_dependencies == 0;
    188 }
    189 
    190 void ModuleRegistry::Load(Isolate* isolate, scoped_ptr<PendingModule> pending) {
    191   if (!pending->id.empty() && available_modules_.count(pending->id))
    192     return;  // We've already loaded this module.
    193 
    194   uint32_t argc = static_cast<uint32_t>(pending->dependencies.size());
    195   std::vector<v8::Handle<Value> > argv(argc);
    196   for (uint32_t i = 0; i < argc; ++i)
    197     argv[i] = GetModule(isolate, pending->dependencies[i]);
    198 
    199   v8::Handle<Value> module = Local<Value>::New(isolate, pending->factory);
    200 
    201   v8::Handle<Function> factory;
    202   if (ConvertFromV8(isolate, module, &factory)) {
    203     PerContextData* data = PerContextData::From(isolate->GetCurrentContext());
    204     Runner* runner = data->runner();
    205     module = runner->Call(factory, runner->global(), argc,
    206                           argv.empty() ? NULL : &argv.front());
    207     if (pending->id.empty())
    208       ConvertFromV8(isolate, factory->GetScriptOrigin().ResourceName(),
    209                     &pending->id);
    210   }
    211 
    212   RegisterModule(isolate, pending->id, module);
    213 }
    214 
    215 bool ModuleRegistry::AttemptToLoad(Isolate* isolate,
    216                                    scoped_ptr<PendingModule> pending) {
    217   if (!CheckDependencies(pending.get())) {
    218     pending_modules_.push_back(pending.release());
    219     return false;
    220   }
    221   Load(isolate, pending.Pass());
    222   return true;
    223 }
    224 
    225 v8::Handle<v8::Value> ModuleRegistry::GetModule(v8::Isolate* isolate,
    226                                                 const std::string& id) {
    227   v8::Handle<Object> modules = Local<Object>::New(isolate, modules_);
    228   v8::Handle<String> key = StringToSymbol(isolate, id);
    229   DCHECK(modules->HasOwnProperty(key));
    230   return modules->Get(key);
    231 }
    232 
    233 void ModuleRegistry::AttemptToLoadMoreModules(Isolate* isolate) {
    234   bool keep_trying = true;
    235   while (keep_trying) {
    236     keep_trying = false;
    237     PendingModuleVector pending_modules;
    238     pending_modules.swap(pending_modules_);
    239     for (size_t i = 0; i < pending_modules.size(); ++i) {
    240       scoped_ptr<PendingModule> pending(pending_modules[i]);
    241       pending_modules[i] = NULL;
    242       if (AttemptToLoad(isolate, pending.Pass()))
    243         keep_trying = true;
    244     }
    245   }
    246 }
    247 
    248 }  // namespace gin
    249