Chromium Code Reviews| Index: gin/modules/module_registry.cc |
| diff --git a/gin/modules/module_registry.cc b/gin/modules/module_registry.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..63849304366e3e51643ed30c9b47179ab2493b01 |
| --- /dev/null |
| +++ b/gin/modules/module_registry.cc |
| @@ -0,0 +1,188 @@ |
| +// 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 <assert.h> |
| +#include <string> |
| +#include <vector> |
| +#include "gin/arguments.h" |
| +#include "gin/converter.h" |
| +#include "gin/per_isolate_data.h" |
| +#include "gin/wrapper_info.h" |
| + |
| +using v8::Handle; |
| +using v8::Isolate; |
| +using v8::ObjectTemplate; |
| + |
| +namespace gin { |
| + |
| +struct PendingModule { |
| + PendingModule(); |
| + ~PendingModule(); |
| + |
| + std::string id; |
| + std::vector<std::string> dependencies; |
| + v8::Persistent<v8::Value> factory; |
| +}; |
| + |
| +namespace { |
| + |
| +void Define(const v8::FunctionCallbackInfo<v8::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; |
| + Handle<v8::Value> factory; |
| + |
| + if (args.PeekNext()->IsString()) |
| + args.GetNext(&id); |
| + if (args.PeekNext()->IsArray()) |
| + args.GetNext(&dependencies); |
| + if (!args.GetNext(&factory)) |
|
Aaron Boodman
2013/11/15 18:56:06
"Factory" seems like a weird name for the function
abarth-chromium
2013/11/15 19:13:37
In AMD, you can either supply a function or an obj
|
| + return args.ThrowError(); |
| + |
| + 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(), pending); |
| +} |
| + |
| +WrapperInfo g_wrapper_info = {}; |
| + |
| +v8::Local<v8::FunctionTemplate> GetDefineTemplate(v8::Isolate* isolate) { |
| + PerIsolateData* data = PerIsolateData::From(isolate); |
| + v8::Local<v8::FunctionTemplate> templ = data->GetFunctionTemplate( |
| + &g_wrapper_info); |
| + if (templ.IsEmpty()) { |
| + templ = v8::FunctionTemplate::New(Define); |
| + templ->SetLength(3); |
|
Aaron Boodman
2013/11/15 18:56:06
I don't think this is really necessary, fwiw.
abarth-chromium
2013/11/15 19:13:37
Ok, I'll remove it. It's visible in JavaScript, b
|
| + data->SetFunctionTemplate(&g_wrapper_info, templ); |
| + } |
| + return templ; |
| +} |
| + |
| +} // namespace gin |
| + |
| + |
| +PendingModule::PendingModule() { |
| +} |
| + |
| +PendingModule::~PendingModule() { |
| + factory.Reset(); |
| +} |
| + |
| +ModuleRegistry::ModuleRegistry(v8::Isolate* isolate) |
| + : modules_(isolate, v8::Object::New()) { |
| +} |
| + |
| +ModuleRegistry::~ModuleRegistry() { |
| + for (PendingModuleList::iterator it = pending_modules_.begin(); |
| + it != pending_modules_.end(); ++it) { |
| + delete *it; |
| + } |
| + modules_.Reset(); |
| +} |
| + |
| +void ModuleRegistry::RegisterGlobals(v8::Isolate* isolate, |
| + Handle<v8::ObjectTemplate> templ) { |
| + templ->Set(StringToSymbol(isolate, "define"), GetDefineTemplate(isolate)); |
| +} |
| + |
| +void ModuleRegistry::AddBuiltinModule(Isolate* isolate, |
| + const std::string& id, |
| + Handle<ObjectTemplate> templ) { |
| + assert(!id.empty()); |
| + Handle<v8::Object> modules = v8::Local<v8::Object>::New(isolate, modules_); |
| + modules->Set(StringToV8(isolate, id), templ->NewInstance()); |
| +} |
| + |
| +ModuleRegistry* ModuleRegistry::From(Handle<v8::Context> context) { |
| + v8::Isolate* isolate = context->GetIsolate(); |
| + Handle<v8::String> key = Key(isolate); |
| + Handle<v8::Value> encoded = context->Global()->GetHiddenValue(key); |
|
Aaron Boodman
2013/11/15 18:56:06
Why is this variable called "encoded"?
abarth-chromium
2013/11/15 19:13:37
Just because it's a pointer encoded into a v8 obje
|
| + if (encoded.IsEmpty() || !encoded->IsExternal()) { |
| + PerContextData* data = PerContextData::From(context); |
| + if (!data) |
| + return NULL; |
| + ModuleRegistry* registry = new ModuleRegistry(isolate); |
| + context->Global()->SetHiddenValue(key, v8::External::New(registry)); |
| + data->AddSupplement(registry); |
| + return registry; |
| + } |
| + Handle<v8::External> external = Handle<v8::External>::Cast(encoded); |
| + return static_cast<ModuleRegistry*>(external->Value()); |
| +} |
| + |
| +void ModuleRegistry::AddPendingModule(v8::Isolate* isolate, |
| + PendingModule* pending) { |
| + if (AttemptToLoad(isolate, pending)) |
| + AttemptToLoadPendingModules(isolate); |
| +} |
| + |
| +Handle<v8::String> ModuleRegistry::Key(v8::Isolate* isolate) { |
|
Aaron Boodman
2013/11/15 18:56:06
::GetHiddenValueKey(). Also, this could just be a
abarth-chromium
2013/11/15 19:13:37
Will do.
|
| + return StringToSymbol(isolate, "::gin::ModuleRegistry"); |
| +} |
| + |
| +void ModuleRegistry::Detach(Handle<v8::Context> context) { |
| + context->Global()->SetHiddenValue(Key(context->GetIsolate()), |
| + Handle<v8::Value>()); |
| +} |
| + |
| +bool ModuleRegistry::AttemptToLoad(v8::Isolate* isolate, |
| + PendingModule* pending) { |
| + Handle<v8::Object> modules = v8::Local<v8::Object>::New(isolate, modules_); |
| + Handle<v8::String> key = StringToV8(isolate, pending->id); |
| + |
| + if (!pending->id.empty() && modules->HasOwnProperty(key)) { |
| + // We've already loaded a module with this name. Ignore the new one. |
| + delete pending; |
| + return true; |
| + } |
| + |
| + size_t argc = pending->dependencies.size(); |
| + std::vector<Handle<v8::Value> > argv(argc); |
| + for (size_t i = 0; i < argc; ++i) { |
| + Handle<v8::String> key = StringToV8(isolate, pending->dependencies[i]); |
| + if (!modules->HasOwnProperty(key)) { |
| + pending_modules_.push_back(pending); |
| + return false; |
| + } |
| + argv[i] = modules->Get(key); |
| + } |
| + |
| + Handle<v8::Value> module = v8::Local<v8::Value>::New( |
| + isolate, pending->factory); |
| + |
| + Handle<v8::Function> factory; |
| + if (ConvertFromV8(module, &factory)) { |
| + v8::Handle<v8::Object> global = isolate->GetCurrentContext()->Global(); |
| + module = factory->Call(global, argc, argv.data()); |
| + // TODO(abarth): What should we do with exceptions? |
| + } |
| + |
| + if (!pending->id.empty() && !module.IsEmpty()) |
| + modules->Set(key, module); |
| + |
| + delete pending; |
| + return true; |
| +} |
| + |
| +void ModuleRegistry::AttemptToLoadPendingModules(v8::Isolate* isolate) { |
| + PendingModuleList pending_modules; |
| + pending_modules.swap(pending_modules_); |
| + for (PendingModuleList::iterator it = pending_modules.begin(); |
| + it != pending_modules.end(); ++it) { |
| + AttemptToLoad(isolate, *it); |
| + } |
| +} |
| + |
| +} // namespace gin |