зеркало из https://github.com/microsoft/napajs.git
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:
Родитель
112f1bd6ba
Коммит
7daa633a32
|
@ -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>
|
||||
|
|
Загрузка…
Ссылка в новой задаче