普通文本  |  249行  |  7.63 KB

// 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 <string>
#include <vector>

#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<std::string> dependencies;
  Persistent<Value> factory;
};

PendingModule::PendingModule() {
}

PendingModule::~PendingModule() {
  factory.Reset();
}

namespace {

void Define(const v8::FunctionCallbackInfo<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;
  v8::Handle<Value> factory;

  if (args.PeekNext()->IsString())
    args.GetNext(&id);
  if (args.PeekNext()->IsArray())
    args.GetNext(&dependencies);
  if (!args.GetNext(&factory))
    return args.ThrowError();

  scoped_ptr<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.Pass());
}

WrapperInfo g_wrapper_info = { kEmbedderNativeGin };

Local<FunctionTemplate> GetDefineTemplate(Isolate* isolate) {
  PerIsolateData* data = PerIsolateData::From(isolate);
  Local<FunctionTemplate> templ = data->GetFunctionTemplate(
      &g_wrapper_info);
  if (templ.IsEmpty()) {
    templ = FunctionTemplate::New(isolate, Define);
    data->SetFunctionTemplate(&g_wrapper_info, templ);
  }
  return templ;
}

v8::Handle<String> 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<ObjectTemplate> templ) {
  templ->Set(StringToSymbol(isolate, "define"), GetDefineTemplate(isolate));
}

ModuleRegistry* ModuleRegistry::From(v8::Handle<Context> context) {
  Isolate* isolate = context->GetIsolate();
  v8::Handle<String> key = GetHiddenValueKey(isolate);
  v8::Handle<Value> value = context->Global()->GetHiddenValue(key);
  v8::Handle<External> 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<ContextSupplement>(registry));
    return registry;
  }
  return static_cast<ModuleRegistry*>(external->Value());
}

void ModuleRegistry::AddBuiltinModule(Isolate* isolate,
                                      const std::string& id,
                                      v8::Handle<ObjectTemplate> templ) {
  DCHECK(!id.empty());
  RegisterModule(isolate, id, templ->NewInstance());
}

void ModuleRegistry::AddPendingModule(Isolate* isolate,
                                      scoped_ptr<PendingModule> 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<Value> module) {
  if (id.empty() || module.IsEmpty())
    return;

  unsatisfied_dependencies_.erase(id);
  available_modules_.insert(id);
  v8::Handle<Object> modules = Local<Object>::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) {
  context->Global()->SetHiddenValue(GetHiddenValueKey(context->GetIsolate()),
                                    v8::Handle<Value>());
}

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<PendingModule> pending) {
  if (!pending->id.empty() && available_modules_.count(pending->id))
    return;  // We've already loaded this module.

  uint32_t argc = static_cast<uint32_t>(pending->dependencies.size());
  std::vector<v8::Handle<Value> > argv(argc);
  for (uint32_t i = 0; i < argc; ++i)
    argv[i] = GetModule(isolate, pending->dependencies[i]);

  v8::Handle<Value> module = Local<Value>::New(isolate, pending->factory);

  v8::Handle<Function> 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<PendingModule> pending) {
  if (!CheckDependencies(pending.get())) {
    pending_modules_.push_back(pending.release());
    return false;
  }
  Load(isolate, pending.Pass());
  return true;
}

v8::Handle<v8::Value> ModuleRegistry::GetModule(v8::Isolate* isolate,
                                                const std::string& id) {
  v8::Handle<Object> modules = Local<Object>::New(isolate, modules_);
  v8::Handle<String> 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<PendingModule> pending(pending_modules[i]);
      pending_modules[i] = NULL;
      if (AttemptToLoad(isolate, pending.Pass()))
        keep_trying = true;
    }
  }
}

}  // namespace gin