Merged PR 219596: Add process.binding()

### Module loader has three kinds of core modules.
#### Built-in:
* It can be accessed with/without require() such as console, process, but can't be with process.binding().
* It's cached at both binding and module cache.
* If javascript core file exists, it overrides binary core module.
#### Binary core module:
* It can be accessed with only process.binding(), not with require().
* It's cached at only binding cache.
#### Javascript core module:
* It exists as Javascript file at './lib' or '../lib' directory.
* It can be accessed with only require() and cached at only module cache.
* If javascript core file exists, it overrides binary core module.
This commit is contained in:
Sunghoon Choi 2017-03-28 22:34:58 +00:00 коммит произвёл Sunghoon Choi
Родитель 112f1bd6ba
Коммит 7daa633a32
21 изменённых файлов: 363 добавлений и 69 удалений

Просмотреть файл

@ -26,6 +26,7 @@
<Copy SourceFiles="..\node\$(O)\addon.node" DestinationFolder="$(PackageOutPath)\bin" ContinueOnError="false" SkipUnchangedFiles="true" />
<Copy SourceFiles="..\napa\$(O)\addon.napa" DestinationFolder="$(PackageOutPath)\bin" ContinueOnError="false" SkipUnchangedFiles="true" />
<Copy SourceFiles="$(NapaVanillaRoot)\src\$(O)\napa.dll" DestinationFolder="$(PackageOutPath)\bin" ContinueOnError="false" SkipUnchangedFiles="true" />
<Copy SourceFiles="$(NapaVanillaRoot)\src\module\core-modules.json" DestinationFolder="$(PackageOutPath)\bin" ContinueOnError="false" SkipUnchangedFiles="true" />
<Copy SourceFiles="napajs.d.ts" DestinationFolder="$(PackageOutPath)\types" ContinueOnError="false" SkipUnchangedFiles="true" />
<Copy SourceFiles="napa-cl.js" DestinationFolder="$(PackageOutPath)\tools" ContinueOnError="false" SkipUnchangedFiles="true" />
<Copy SourceFiles="napa-cl.bat" DestinationFolder="$(PackageOutPath)\tools" ContinueOnError="false" SkipUnchangedFiles="true" />

Просмотреть файл

@ -8,7 +8,8 @@
using namespace napa;
using namespace napa::module;
BinaryModuleLoader::BinaryModuleLoader(BuiltInsSetter builtInsSetter) : _builtInsSetter(std::move(builtInsSetter)) {}
BinaryModuleLoader::BinaryModuleLoader(BuiltInModulesSetter builtInModulesSetter)
: _builtInModulesSetter(std::move(builtInModulesSetter)) {}
bool BinaryModuleLoader::TryGet(const std::string& path, v8::Local<v8::Object>& module) {
auto isolate = v8::Isolate::GetCurrent();
@ -34,7 +35,7 @@ bool BinaryModuleLoader::TryGet(const std::string& path, v8::Local<v8::Object>&
context->SetSecurityToken(v8::Undefined(isolate));
v8::Context::Scope contextScope(context);
_builtInsSetter(context);
_builtInModulesSetter(context);
module = scope.Escape(module_loader_helpers::ExportModule(context->Global(), napaModule->initializer));
return true;

Просмотреть файл

@ -1,6 +1,5 @@
#pragma once
#include "module-cache.h"
#include "module-file-loader.h"
#include <boost/shared_ptr.hpp>
@ -16,7 +15,8 @@ namespace module {
public:
/// <summary> Constructor. </summary>
BinaryModuleLoader(BuiltInsSetter builtInsSetter);
/// <param name="builtsInSetter"> Built-in modules registerer. </param>
BinaryModuleLoader(BuiltInModulesSetter builtInModulesSetter);
/// <summary> It loads a module from binary file. </summary>
/// <param name="path"> Module path called by require(). </param>
@ -27,7 +27,7 @@ namespace module {
private:
/// Built-in modules registerer.
BuiltInsSetter _builtInsSetter;
BuiltInModulesSetter _builtInModulesSetter;
/// <summary> It keeps binary modules loaded. </summary>
/// <remarks> boost::dll unload a shared library when no reference is there. </remarks>

Просмотреть файл

@ -1,10 +1,44 @@
#include "core-module-loader.h"
#include "javascript-module-loader.h"
#include "module-cache.h"
#include "module-loader-helpers.h"
#include <boost/filesystem.hpp>
#include <sstream>
using namespace napa;
using namespace napa::module;
CoreModuleLoader::CoreModuleLoader(ModuleCache& cache) : _cache(cache) {}
namespace {
const std::string CORE_MODULE_EXTENSION = ".js";
const std::string CORE_MODULE_DIRECTORY = "lib";
} // End of anonymous namespace.
CoreModuleLoader::CoreModuleLoader(BuiltInModulesSetter builtInModulesSetter,
ModuleCache& moduleCache,
ModuleCache& bindingCache)
: JavascriptModuleLoader(std::move(builtInModulesSetter), moduleCache), _bindingCache(bindingCache) {}
bool CoreModuleLoader::TryGet(const std::string& name, v8::Local<v8::Object>& module) {
return _cache.TryGet(name, module);
boost::filesystem::path basePath(module_loader_helpers::GetModuleRootDirectory());
auto fileName = name + CORE_MODULE_EXTENSION;
// Check ./lib or ../lib directory only
auto fullPath = basePath / CORE_MODULE_DIRECTORY / fileName;
if (boost::filesystem::is_regular_file(fullPath)) {
// Load javascript core module from a file at ./lib directory.
return JavascriptModuleLoader::TryGet(fullPath.string(), module);
}
fullPath = basePath.parent_path() / CORE_MODULE_DIRECTORY / fileName;
if (boost::filesystem::is_regular_file(fullPath)) {
// Load javascript core module from a file at ../lib directory.
return JavascriptModuleLoader::TryGet(fullPath.string(), module);
}
// Return binary core module if exists.
return _bindingCache.TryGet(name, module);
}

Просмотреть файл

@ -1,7 +1,6 @@
#pragma once
#include "module-cache.h"
#include "module-file-loader.h"
#include "javascript-module-loader.h"
#include <string>
@ -9,11 +8,16 @@ namespace napa {
namespace module {
/// <summary> It loads a core module. </summary>
class CoreModuleLoader : public ModuleFileLoader {
class CoreModuleLoader : public JavascriptModuleLoader {
public:
/// <summary> Constructor. </summary>
CoreModuleLoader(ModuleCache& cache);
/// <param name="builtsInSetter"> Built-in modules registerer. </param>
/// <param name="moduleCache"> Cache for all modules. </param>
/// <param name="bindingCache"> Cache for binding core binary modules. </param>
CoreModuleLoader(BuiltInModulesSetter builtInModulesSetter,
ModuleCache& moduleCache,
ModuleCache& bindingCache);
/// <summary> It loads a core module. </summary>
/// <param name="name"> Core module name. </param>
@ -23,8 +27,8 @@ namespace module {
private:
/// Module cache instance.
ModuleCache& _cache;
/// Cache instance for binding binary core modules.
ModuleCache& _bindingCache;
};
} // End of namespace module.

Просмотреть файл

@ -0,0 +1,6 @@
[
{
"name": "events",
"type": "core"
}
]

Просмотреть файл

@ -1,4 +1,5 @@
#include "javascript-module-loader.h"
#include "module-cache.h"
#include "module-loader-helpers.h"
#include <napa/v8-helpers.h>
@ -6,8 +7,8 @@
using namespace napa;
using namespace napa::module;
JavascriptModuleLoader::JavascriptModuleLoader(BuiltInsSetter builtInsSetter, ModuleCache& cache)
: _builtInsSetter(std::move(builtInsSetter)) , _cache(cache) {}
JavascriptModuleLoader::JavascriptModuleLoader(BuiltInModulesSetter builtInModulesSetter, ModuleCache& moduleCache)
: _builtInModulesSetter(std::move(builtInModulesSetter)) , _moduleCache(moduleCache) {}
bool JavascriptModuleLoader::TryGet(const std::string& path, v8::Local<v8::Object>& module) {
auto isolate = v8::Isolate::GetCurrent();
@ -23,10 +24,10 @@ bool JavascriptModuleLoader::TryGet(const std::string& path, v8::Local<v8::Objec
context->SetSecurityToken(v8::Undefined(isolate));
v8::Context::Scope contextScope(context);
_builtInsSetter(context);
_builtInModulesSetter(context);
// To prevent cycle, cache unloaded module first.
_cache.Insert(path, module_loader_helpers::ExportModule(context->Global(), nullptr));
_moduleCache.Upsert(path, module_loader_helpers::ExportModule(context->Global(), nullptr));
v8::TryCatch tryCatch;
{

Просмотреть файл

@ -1,6 +1,5 @@
#pragma once
#include "module-cache.h"
#include "module-file-loader.h"
#include <string>
@ -8,12 +7,17 @@
namespace napa {
namespace module {
// forward declaration.
class ModuleCache;
/// <summary> It loads a module from javascript file. </summary>
class JavascriptModuleLoader : public ModuleFileLoader {
public:
/// <summary> Constructor. </summary>
JavascriptModuleLoader(BuiltInsSetter builtInsSetter, ModuleCache& cache);
/// <param name="builtsInSetter"> Built-in modules registerer. </param>
/// <param name="moduleCache"> Cache for all modules. </param>
JavascriptModuleLoader(BuiltInModulesSetter builtInModulesSetter, ModuleCache& moduleCache);
/// <summary> It loads a module from javascript file. </summary>
/// <param name="path"> Module path called by require(). </param>
@ -24,10 +28,10 @@ namespace module {
private:
/// Built-in modules registerer.
BuiltInsSetter _builtInsSetter;
BuiltInModulesSetter _builtInModulesSetter;
/// Module cache instance.
ModuleCache& _cache;
ModuleCache& _moduleCache;
};
} // End of namespace module.

Просмотреть файл

@ -17,7 +17,7 @@ ModuleCache::ModuleCache() : _impl(std::make_unique<ModuleCache::ModuleCacheImpl
ModuleCache::~ModuleCache() = default;
void ModuleCache::Insert(const std::string& path, v8::Local<v8::Object> module) {
void ModuleCache::Upsert(const std::string& path, v8::Local<v8::Object> module) {
if (module.IsEmpty()) {
return;
}

Просмотреть файл

@ -25,10 +25,10 @@ namespace module {
ModuleCache(ModuleCache&&) = default;
ModuleCache& operator=(ModuleCache&&) = default;
/// <summary> It caches a module using path as a key. </summary>
/// <summary> It inserts or updates a module into cache using path as a key. </summary>
/// <param name="path"> The module name path. </param>
/// <param name="module"> V8 representative javascript object to be cached. </param>
void Insert(const std::string& path, v8::Local<v8::Object> module);
void Upsert(const std::string& path, v8::Local<v8::Object> module);
/// <summary> A helper to load a module from cache. </summary>
/// <param name="path"> Full path of javascript or napa module. </param>

Просмотреть файл

@ -4,12 +4,11 @@
#include <functional>
#include <string>
#include <vector>
namespace napa {
namespace module {
using BuiltInsSetter = std::function<void (v8::Local<v8::Context> context)>;
using BuiltInModulesSetter = std::function<void (v8::Local<v8::Context> context)>;
/// <summary> Interface to load a module from file. </summary>
class ModuleFileLoader {

Просмотреть файл

@ -1,19 +1,18 @@
#include "module-loader-helpers.h"
#include "core-modules/file-system-helpers.h"
#include <napa-log.h>
#include <napa/v8-helpers.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/dll.hpp>
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/property_tree/json_parser.hpp>
using namespace napa;
using namespace napa::module;
namespace {
std::string _moduleRootDirectory = boost::dll::this_line_location().parent_path().string();
} // End of anonymous namespace.
v8::Local<v8::Object> module_loader_helpers::ExportModule(v8::Local<v8::Object> object,
const napa::module::ModuleInitializer& initializer) {
auto isolate = v8::Isolate::GetCurrent();
@ -39,13 +38,18 @@ std::string module_loader_helpers::GetCurrentContextDirectory() {
if (contextObject.IsEmpty()
|| contextObject->IsNull()
|| !contextObject->Has(dirPropertyName)) {
return _moduleRootDirectory;
return GetModuleRootDirectory();
}
v8::String::Utf8Value callingPath(contextObject->Get(dirPropertyName));
return std::string(*callingPath);
}
std::string module_loader_helpers::GetModuleRootDirectory() {
static std::string moduleRootDirectory = boost::dll::this_line_location().parent_path().string();
return moduleRootDirectory;
}
void module_loader_helpers::SetContextModulePath(v8::Local<v8::Object> exports) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
@ -54,7 +58,7 @@ void module_loader_helpers::SetContextModulePath(v8::Local<v8::Object> exports)
(void)exports->CreateDataProperty(context,
v8_helpers::MakeV8String(isolate, "__dirname"),
v8_helpers::MakeV8String(isolate, _moduleRootDirectory.c_str()));
v8_helpers::MakeV8String(isolate, GetModuleRootDirectory().c_str()));
(void)exports->CreateDataProperty(context, v8_helpers::MakeV8String(isolate, "__filename"), v8::Null(isolate));
}
@ -76,7 +80,7 @@ v8::Local<v8::Context> module_loader_helpers::SetUpModuleContext(const std::stri
if (path.empty()) {
(void)module->Set(v8_helpers::MakeV8String(isolate, "__dirname"),
v8_helpers::MakeV8String(isolate, _moduleRootDirectory.c_str()));
v8_helpers::MakeV8String(isolate, GetModuleRootDirectory().c_str()));
(void)module->Set(v8_helpers::MakeV8String(isolate, "__filename"), v8::Null(isolate));
} else {
boost::filesystem::path current(path);
@ -97,6 +101,37 @@ v8::Local<v8::Context> module_loader_helpers::SetUpModuleContext(const std::stri
return scope.Escape(context);
}
std::vector<module_loader_helpers::CoreModuleInfo> module_loader_helpers::ReadCoreModulesJson() {
static const std::string CORE_MODULES_JSON_PATH =
(boost::filesystem::path(GetModuleRootDirectory()) / "core-modules.json").string();
if (!boost::filesystem::exists(CORE_MODULES_JSON_PATH)) {
return std::vector<CoreModuleInfo>();
}
std::vector<CoreModuleInfo> coreModuleInfos;
// Reserve capacity to help avoiding too frequent allocation.
coreModuleInfos.reserve(16);
boost::property_tree::ptree modules;
try {
boost::property_tree::json_parser::read_json(CORE_MODULES_JSON_PATH, modules);
for (const auto& value : modules) {
const auto& module = value.second;
coreModuleInfos.emplace_back(module.get<std::string>("name"),
boost::iequals(module.get<std::string>("type"), "builtin"));
}
} catch (const std::exception& ex) {
LOG_ERROR("ModuleLoader", ex.what());
return std::vector<CoreModuleInfo>();
}
return coreModuleInfos;
}
v8::Local<v8::String> module_loader_helpers::ReadModuleFile(const std::string& path) {
auto isolate = v8::Isolate::GetCurrent();
v8::EscapableHandleScope scope(isolate);

Просмотреть файл

@ -6,6 +6,19 @@ namespace napa {
namespace module {
namespace module_loader_helpers {
/// <summary> Core module information from 'core-modules.json'. </summary>
struct CoreModuleInfo {
/// <summary> Core module name. </summary>
std::string name;
/// <summary> True if it's a built-in module. False, otherwise. </summary>
bool isBuiltIn;
/// <summary> Constructor. </summary>
CoreModuleInfo(std::string name, bool isBuiltIn)
: name(std::move(name)), isBuiltIn(isBuiltIn) {}
};
/// <summary> It exports loaded module. </summary>
/// <param name="object"> Loaded javascript object. </param>
/// <param name="initializer"> Callback function to initialize a module. </param>
@ -17,6 +30,10 @@ namespace module_loader_helpers {
/// <returns> Directory of context. If called from external string, module root directory will be returned. </returns>
std::string GetCurrentContextDirectory();
/// <summary> It returns the directory of napa runtime. </summary>
/// <returns> Directory of napa runtime. </returns>
std::string GetModuleRootDirectory();
/// <summary> Set __dirname and __filename. </summary>
/// <param name="exports"> Object to set modue paths. </param>
void SetContextModulePath(v8::Local<v8::Object> exports);
@ -26,6 +43,9 @@ namespace module_loader_helpers {
/// <returns> V8 context object. </returns>
v8::Local<v8::Context> SetUpModuleContext(const std::string& path);
/// <summary> It reads javascript core module information. </summary>
std::vector<CoreModuleInfo> ReadCoreModulesJson();
/// <summary> It reads a module file to javascript string. </summary>
/// <param name="path"> File path to read. </param>
/// <returns> V8 string containing file content. </returns>

Просмотреть файл

@ -10,15 +10,31 @@
#include "core-modules/core-modules.h"
#include <napa-log.h>
// This is not a module extension, so define this macro to use V8 common macros.
#define NAPA_MODULE_EXTENSION
#include <napa-module.h>
#include <napa-log.h>
#include <unordered_set>
using namespace napa;
using namespace napa::module;
/// <summary> Implementation of module loader. </summary>
/// <remarks>
/// It has three kinds of core modules.
/// Built-in module:
/// It can be accessed with/without require() such as console, process, but can't be with process.binding().
/// It's cached at both binding and module cache.
/// If javascript core file exists, it overrides binary core module.
/// Binary core module:
/// It can be accessed with only process.binding(), not with require().
/// It's cached at only binding cache.
/// Javascript core module:
/// It exists as Javascript file at './lib' or '../lib' directory.
/// It can be accessed with only require() and cached at only module cache.
/// If javascript core file exists, it overrides binary core module.
/// </remarks>
class ModuleLoader::ModuleLoaderImpl {
public:
@ -35,6 +51,10 @@ private:
/// <param name="args"> Module name. </param>
static void ResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
/// <summary> Callback to bind core binary module. </summary>
/// <param name="args"> Module name. </param>
static void BindingCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
/// <summary> It loads a module. </summary>
/// <param name="path"> Module path called by require(). </param>
/// <param name="args"> V8 argument to return module object. </param>
@ -43,27 +63,39 @@ private:
/// <summary> It registers a binary core module. </summary>
/// <param name="name"> Module name. </param>
/// <param name="isBuiltIn"> True if it's a built-in module, which doesn't need require() to call. </param>
/// <param name="isBuiltInModule"> True if it's a built-in module, which doesn't need require() to call. </param>
/// <param name="initializer"> Module initialization function. </param>
void LoadBinaryCoreModule(const char* name,
bool isBuiltIn,
bool isBuiltInModule,
const napa::module::ModuleInitializer& initializer);
/// <summary> It sets up built-in modules, which doesn't need to be called by require(). </summary>
/// <summary> It bootstraps core modules, which can be loaded with or without require(). </summary>
/// <param name="context"> V8 context. </param>
void SetUpBuiltIns(v8::Local<v8::Context> context);
void BootstrapCoreModules(v8::Local<v8::Context> context);
/// <summary> It sets up built-in modules at each module's' context. </summary>
/// <param name="context"> V8 context. </param>
void SetUpBuiltInModules(v8::Local<v8::Context> context);
/// <summary> It sets up require function. </summary>
void SetUpRequire(v8::Local<v8::Context> context);
/// <summary> It adds the extra functions, which access module loader's function, into built-in modules. <summary>
/// <param name="context"> V8 context. </param>
/// <remarks> It assumes that calling built-in modules are already loaded into a context. </remarks>
void DecorateBuiltInModules(v8::Local<v8::Context> context);
/// <summary> Module cache to avoid module loading overhead. </summary>
ModuleCache _cache;
ModuleCache _moduleCache;
/// <summary> Cache for core binary modules, which can be accessed by process.binding(). </summary>
ModuleCache _bindingCache;
/// <summary> Module resolver to resolve module path. </summary>
ModuleResolver _resolver;
/// <summary> Built-in module list. </summary>
std::vector<std::string> _builtIns;
std::unordered_set<std::string> _builtInNames;
/// <summary> Module loaders. </summary>
std::array<std::unique_ptr<ModuleFileLoader>, static_cast<size_t>(ModuleType::END_OF_MODULE_TYPE)> _loaders;
@ -82,17 +114,17 @@ ModuleLoader::ModuleLoader() : _impl(std::make_unique<ModuleLoader::ModuleLoader
ModuleLoader::~ModuleLoader() = default;
ModuleLoader::ModuleLoaderImpl::ModuleLoaderImpl() {
auto builtInsSetter = [this](v8::Local<v8::Context> context) {
SetUpBuiltIns(context);
auto builtInModulesSetter = [this](v8::Local<v8::Context> context) {
SetUpBuiltInModules(context);
};
// Set up module loaders for each module type.
_loaders = {{
nullptr,
std::make_unique<CoreModuleLoader>(_cache),
std::make_unique<JavascriptModuleLoader>(builtInsSetter, _cache),
std::make_unique<CoreModuleLoader>(builtInModulesSetter, _moduleCache, _bindingCache),
std::make_unique<JavascriptModuleLoader>(builtInModulesSetter, _moduleCache),
std::make_unique<JsonModuleLoader>(),
std::make_unique<BinaryModuleLoader>(builtInsSetter)
std::make_unique<BinaryModuleLoader>(builtInModulesSetter)
}};
auto isolate = v8::Isolate::GetCurrent();
@ -106,8 +138,8 @@ ModuleLoader::ModuleLoaderImpl::ModuleLoaderImpl() {
// Initialize core modules listed in core-modules.h.
INITIALIZE_CORE_MODULES(LoadBinaryCoreModule)
// Set up built-in modules at this context.
SetUpBuiltIns(context);
// Bootstrap core modules.
BootstrapCoreModules(context);
}
void ModuleLoader::ModuleLoaderImpl::RequireCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
@ -119,7 +151,7 @@ void ModuleLoader::ModuleLoaderImpl::RequireCallback(const v8::FunctionCallbackI
"Invalid arguments");
auto moduleLoader = reinterpret_cast<ModuleLoader*>(IsolateData::Get(IsolateDataId::MODULE_LOADER));
JS_ASSERT(isolate, moduleLoader != nullptr, "Module loader is not initialized");
JS_ENSURE(isolate, moduleLoader != nullptr, "Module loader is not initialized");
v8::String::Utf8Value path(args[0]);
moduleLoader->_impl->RequireModule(*path, args);
@ -132,7 +164,7 @@ void ModuleLoader::ModuleLoaderImpl::ResolveCallback(const v8::FunctionCallbackI
CHECK_ARG(isolate, args.Length() == 1 && args[0]->IsString(), "Invalid arguments");
auto moduleLoader = reinterpret_cast<ModuleLoader*>(IsolateData::Get(IsolateDataId::MODULE_LOADER));
JS_ASSERT(isolate, moduleLoader != nullptr, "Module loader is not initialized");
JS_ENSURE(isolate, moduleLoader != nullptr, "Module loader is not initialized");
v8::String::Utf8Value path(args[0]);
auto contextDir = module_loader_helpers::GetCurrentContextDirectory();
@ -141,6 +173,26 @@ void ModuleLoader::ModuleLoaderImpl::ResolveCallback(const v8::FunctionCallbackI
args.GetReturnValue().Set(v8_helpers::MakeV8String(isolate, resolvedPath.fullPath));
}
void ModuleLoader::ModuleLoaderImpl::BindingCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
CHECK_ARG(isolate, args.Length() == 1 && args[0]->IsString(), "Invalid arguments");
auto moduleLoader = reinterpret_cast<ModuleLoader*>(IsolateData::Get(IsolateDataId::MODULE_LOADER));
JS_ENSURE(isolate, moduleLoader != nullptr, "Module loader is not initialized");
v8::String::Utf8Value name(args[0]);
v8::Local<v8::Object> module;
if (moduleLoader->_impl->_bindingCache.TryGet(*name, module)) {
args.GetReturnValue().Set(module);
return;
}
args.GetReturnValue().SetUndefined();
}
void ModuleLoader::ModuleLoaderImpl::RequireModule(const char* path, const v8::FunctionCallbackInfo<v8::Value>& args) {
if (path == nullptr) {
args.GetReturnValue().SetUndefined();
@ -158,13 +210,13 @@ void ModuleLoader::ModuleLoaderImpl::RequireModule(const char* path, const v8::F
}
v8::Local<v8::Object> module;
if (_cache.TryGet(moduleInfo.fullPath, module)) {
if (_moduleCache.TryGet(moduleInfo.fullPath, module)) {
args.GetReturnValue().Set(module);
return;
}
auto& loader = _loaders[static_cast<size_t>(moduleInfo.type)];
JS_ASSERT(isolate, loader != nullptr, "No proper module loader is defined");
JS_ENSURE(isolate, loader != nullptr, "No proper module loader is defined");
auto succeeded = loader->TryGet(moduleInfo.fullPath, module);
if (!succeeded) {
@ -172,13 +224,13 @@ void ModuleLoader::ModuleLoaderImpl::RequireModule(const char* path, const v8::F
return;
}
_cache.Insert(moduleInfo.fullPath, module);
_moduleCache.Upsert(moduleInfo.fullPath, module);
args.GetReturnValue().Set(module);
}
void ModuleLoader::ModuleLoaderImpl::LoadBinaryCoreModule(
const char* name,
bool isBuiltIn,
bool isBuiltInModule,
const napa::module::ModuleInitializer& initializer) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
@ -190,37 +242,74 @@ void ModuleLoader::ModuleLoaderImpl::LoadBinaryCoreModule(
context->SetSecurityToken(v8::Undefined(isolate));
v8::Context::Scope contextScope(context);
auto module = module_loader_helpers::ExportModule(context->Global(), initializer);
// Put core module into cache.
// This makes the same behavior with node.js, i.e. it must be loaded by 'require'.
_cache.Insert(name, module);
// Put it into module resolver to prevent from resolving as user module.
_resolver.SetAsCoreModule(name);
if (isBuiltIn) {
_builtIns.emplace_back(name);
auto module = module_loader_helpers::ExportModule(context->Global(), initializer);
if (isBuiltInModule) {
_builtInNames.emplace(name);
// Put core module into cache.
// This makes the same behavior with node.js, i.e. it must be loaded by 'require'.
_moduleCache.Upsert(name, module);
} else {
// Put into binding cache.
// It must be accessed by process.binding(), not by require().
_bindingCache.Upsert(name, module);
}
}
void ModuleLoader::ModuleLoaderImpl::SetUpBuiltIns(v8::Local<v8::Context> context) {
void ModuleLoader::ModuleLoaderImpl::BootstrapCoreModules(v8::Local<v8::Context> context) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
// Set up built-in modules from binaries.
SetUpBuiltInModules(context);
// Load core module information from 'core-modules.json'.
auto coreModuleInfos = module_loader_helpers::ReadCoreModulesJson();
for (auto& info : coreModuleInfos) {
_resolver.SetAsCoreModule(info.name.c_str());
if (info.isBuiltIn) {
_builtInNames.emplace(std::move(info.name));
}
}
auto& coreModuleLoader = _loaders[static_cast<size_t>(ModuleType::CORE)];
// Override built-in modules with javascript file if exists.
for (const auto& name : _builtInNames) {
v8::Local<v8::Object> module;
if (coreModuleLoader->TryGet(name, module)) {
// If javascript core module exists, replace the existing one.
_moduleCache.Upsert(name, module);
(void)context->Global()->Set(context,
v8_helpers::MakeV8String(isolate, name),
module);
}
}
}
void ModuleLoader::ModuleLoaderImpl::SetUpBuiltInModules(v8::Local<v8::Context> context) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
SetUpRequire(context);
auto& coreModuleLoader = _loaders[static_cast<size_t>(ModuleType::CORE)];
// Assume that built-in modules are already cached.
for (const auto& builtIn : _builtIns) {
for (const auto& name : _builtInNames) {
v8::Local<v8::Object> module;
if (coreModuleLoader->TryGet(builtIn, module)) {
if (_moduleCache.TryGet(name, module)) {
(void)context->Global()->CreateDataProperty(context,
v8_helpers::MakeV8String(isolate, builtIn),
v8_helpers::MakeV8String(isolate, name),
module);
}
}
// Can decorate a context after loading up built-in modules.
DecorateBuiltInModules(context);
}
void ModuleLoader::ModuleLoaderImpl::SetUpRequire(v8::Local<v8::Context> context) {
@ -240,4 +329,20 @@ void ModuleLoader::ModuleLoaderImpl::SetUpRequire(v8::Local<v8::Context> context
(void)require->CreateDataProperty(context,
v8_helpers::MakeV8String(isolate, "resolve"),
resolveFunctionTemplate->GetFunction());
}
// If we have more decorations, move them out from this class.
void ModuleLoader::ModuleLoaderImpl::DecorateBuiltInModules(v8::Local<v8::Context> context) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
// Add process.binding()
auto process = context->Global()->Get(context,
v8_helpers::MakeV8String(isolate, "process")).ToLocalChecked()->ToObject();
JS_ENSURE(isolate, !process.IsEmpty(), "Process built-in module doesn't exist");
auto bindingFunctionTemplate = v8::FunctionTemplate::New(isolate, BindingCallback);
(void)process->CreateDataProperty(context,
v8_helpers::MakeV8String(isolate, "binding"),
bindingFunctionTemplate->GetFunction());
}

Просмотреть файл

@ -0,0 +1,11 @@
if (typeof fs != 'undefined') throw newError();
var fs = require('fs');
if (fs.runTest() != 'fs.runTest') throw newError();
if (typeof console == undefined) throw newError();
if (console.runTest() != 'console.runTest') throw newError();
var assert = require('assert');
if (assert.runTest() != 'assert.runTest') throw newError();
true;

Просмотреть файл

@ -0,0 +1,14 @@
[
{
"name": "assert",
"type": "core"
},
{
"name": "console",
"type": "builtin"
},
{
"name": "fs",
"type": "core"
}
]

Просмотреть файл

@ -0,0 +1,5 @@
var assert = exports;
assert.runTest = function() {
return 'assert.runTest';
}

Просмотреть файл

@ -0,0 +1,10 @@
var binding = console;
var console = exports;
console.log = function(message) {
return binding.log(message);
}
console.runTest = function() {
return 'console.runTest';
}

Просмотреть файл

@ -0,0 +1,22 @@
var binding = process.binding('fs');
var fs = exports;
fs.readFileSync = function(path, options) {
return binding.readFileSync(path);
}
fs.writeFileSync = function(path, data, options) {
return binding.writeFileSync(path, data);
}
fs.mkdirSync = function(path, mode) {
return binding.mkdirSync(path);
}
fs.existsSync = function(path) {
return binding.existsSync(path);
}
fs.runTest = function() {
return 'fs.runTest';
}

Просмотреть файл

@ -182,6 +182,13 @@ TEST_CASE("require.resolve", "[module-loader]") {
REQUIRE(result);
}
TEST_CASE("core modules", "[module-loader]") {
auto result = RunScriptFile("core-module-tests.js", [](v8::Local<v8::Value> run) {
return run->BooleanValue();
});
REQUIRE(result);
}
TEST_CASE("process", "[module-loader]") {
std::ostringstream oss;
oss << "process.argv.length;";

Просмотреть файл

@ -81,6 +81,12 @@
<Robocopy Include="$(V8MiscPath)\snapshot_blob.bin">
<DestinationFolder>$(IntermediateOutputPath)</DestinationFolder>
</Robocopy>
<Robocopy Include="core-module-tests.js">
<DestinationFolder>$(IntermediateOutputPath)</DestinationFolder>
</Robocopy>
<Robocopy Include="core-modules.json">
<DestinationFolder>$(IntermediateOutputPath)</DestinationFolder>
</Robocopy>
<Robocopy Include="cycle-a.js">
<DestinationFolder>$(IntermediateOutputPath)</DestinationFolder>
</Robocopy>
@ -117,6 +123,15 @@
<Robocopy Include="toupper.js">
<DestinationFolder>$(IntermediateOutputPath)\test</DestinationFolder>
</Robocopy>
<Robocopy Include="lib\assert.js">
<DestinationFolder>$(IntermediateOutputPath)\lib</DestinationFolder>
</Robocopy>
<Robocopy Include="lib\console.js">
<DestinationFolder>$(IntermediateOutputPath)\lib</DestinationFolder>
</Robocopy>
<Robocopy Include="lib\fs.js">
<DestinationFolder>$(IntermediateOutputPath)\lib</DestinationFolder>
</Robocopy>
<Robocopy Include="tests\node_modules\file2.js">
<DestinationFolder>$(IntermediateOutputPath)\tests\node_modules</DestinationFolder>
</Robocopy>