• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
PendingModule()41 PendingModule::PendingModule() {
42 }
43 
~PendingModule()44 PendingModule::~PendingModule() {
45   factory.Reset();
46 }
47 
48 namespace {
49 
Define(const v8::FunctionCallbackInfo<Value> & info)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 
GetDefineTemplate(Isolate * isolate)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 
GetHiddenValueKey(Isolate * isolate)90 v8::Handle<String> GetHiddenValueKey(Isolate* isolate) {
91   return StringToSymbol(isolate, "::gin::ModuleRegistry");
92 }
93 
94 }  // namespace
95 
ModuleRegistry(Isolate * isolate)96 ModuleRegistry::ModuleRegistry(Isolate* isolate)
97     : modules_(isolate, Object::New()) {
98 }
99 
~ModuleRegistry()100 ModuleRegistry::~ModuleRegistry() {
101   modules_.Reset();
102 }
103 
RegisterGlobals(Isolate * isolate,v8::Handle<ObjectTemplate> templ)104 void ModuleRegistry::RegisterGlobals(Isolate* isolate,
105                                      v8::Handle<ObjectTemplate> templ) {
106   templ->Set(StringToSymbol(isolate, "define"), GetDefineTemplate(isolate));
107 }
108 
From(v8::Handle<Context> context)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 
AddBuiltinModule(Isolate * isolate,const std::string & id,v8::Handle<ObjectTemplate> templ)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 
AddPendingModule(Isolate * isolate,scoped_ptr<PendingModule> pending)133 void ModuleRegistry::AddPendingModule(Isolate* isolate,
134                                       scoped_ptr<PendingModule> pending) {
135   AttemptToLoad(isolate, pending.Pass());
136 }
137 
LoadModule(Isolate * isolate,const std::string & id,LoadModuleCallback callback)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 
RegisterModule(Isolate * isolate,const std::string & id,v8::Handle<Value> module)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 
Detach(v8::Handle<Context> context)172 void ModuleRegistry::Detach(v8::Handle<Context> context) {
173   context->Global()->SetHiddenValue(GetHiddenValueKey(context->GetIsolate()),
174                                     v8::Handle<Value>());
175 }
176 
CheckDependencies(PendingModule * pending)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 
Load(Isolate * isolate,scoped_ptr<PendingModule> pending)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 
AttemptToLoad(Isolate * isolate,scoped_ptr<PendingModule> pending)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 
GetModule(v8::Isolate * isolate,const std::string & id)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 
AttemptToLoadMoreModules(Isolate * isolate)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