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