src: improve module loader readability
Various improvements on readability, performance and conformity to the Node core coding style in the ESM loader C++ code: - `isolate` for the `Isolate*`, `context` for the `Local<Context>` - Less reliance on `auto` where it’s unnecessary/increases cognitive overhead - Shorter paths to failure via `.ToLocal()` & co - Do not keep pending exceptions around e.g. for failed `JSON` parsing - Use `Maybe` types to get explicit error status forwarding - Remove an unnecessary handle scope - Add `nullptr` checks for unwrapped host objects - Remove unused code - Use `CamelCase` for function names - Use `const Foo&` instead of copying values whenever possible - Pass along the `Environment*` explicitly PR-URL: https://github.com/nodejs/node/pull/16536 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
This commit is contained in:
parent
89b3228f4d
commit
e22a8f17a5
@ -185,6 +185,7 @@ class ModuleWrap;
|
|||||||
V(kill_signal_string, "killSignal") \
|
V(kill_signal_string, "killSignal") \
|
||||||
V(length_string, "length") \
|
V(length_string, "length") \
|
||||||
V(mac_string, "mac") \
|
V(mac_string, "mac") \
|
||||||
|
V(main_string, "main") \
|
||||||
V(max_buffer_string, "maxBuffer") \
|
V(max_buffer_string, "maxBuffer") \
|
||||||
V(message_string, "message") \
|
V(message_string, "message") \
|
||||||
V(minttl_string, "minttl") \
|
V(minttl_string, "minttl") \
|
||||||
|
@ -14,7 +14,6 @@ namespace loader {
|
|||||||
using node::url::URL;
|
using node::url::URL;
|
||||||
using node::url::URL_FLAGS_FAILED;
|
using node::url::URL_FLAGS_FAILED;
|
||||||
using v8::Context;
|
using v8::Context;
|
||||||
using v8::EscapableHandleScope;
|
|
||||||
using v8::Function;
|
using v8::Function;
|
||||||
using v8::FunctionCallbackInfo;
|
using v8::FunctionCallbackInfo;
|
||||||
using v8::FunctionTemplate;
|
using v8::FunctionTemplate;
|
||||||
@ -23,15 +22,18 @@ using v8::Integer;
|
|||||||
using v8::IntegrityLevel;
|
using v8::IntegrityLevel;
|
||||||
using v8::Isolate;
|
using v8::Isolate;
|
||||||
using v8::JSON;
|
using v8::JSON;
|
||||||
|
using v8::Just;
|
||||||
using v8::Local;
|
using v8::Local;
|
||||||
using v8::Maybe;
|
using v8::Maybe;
|
||||||
using v8::MaybeLocal;
|
using v8::MaybeLocal;
|
||||||
using v8::Module;
|
using v8::Module;
|
||||||
|
using v8::Nothing;
|
||||||
using v8::Object;
|
using v8::Object;
|
||||||
using v8::Promise;
|
using v8::Promise;
|
||||||
using v8::ScriptCompiler;
|
using v8::ScriptCompiler;
|
||||||
using v8::ScriptOrigin;
|
using v8::ScriptOrigin;
|
||||||
using v8::String;
|
using v8::String;
|
||||||
|
using v8::TryCatch;
|
||||||
using v8::Value;
|
using v8::Value;
|
||||||
|
|
||||||
static const char* const EXTENSIONS[] = {".mjs", ".js", ".json", ".node"};
|
static const char* const EXTENSIONS[] = {".mjs", ".js", ".json", ".node"};
|
||||||
@ -61,7 +63,7 @@ ModuleWrap::~ModuleWrap() {
|
|||||||
void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
|
void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
|
||||||
Isolate* iso = args.GetIsolate();
|
Isolate* isolate = args.GetIsolate();
|
||||||
|
|
||||||
if (!args.IsConstructCall()) {
|
if (!args.IsConstructCall()) {
|
||||||
env->ThrowError("constructor must be called using new");
|
env->ThrowError("constructor must be called using new");
|
||||||
@ -79,7 +81,7 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto source_text = args[0].As<String>();
|
Local<String> source_text = args[0].As<String>();
|
||||||
|
|
||||||
if (!args[1]->IsString()) {
|
if (!args[1]->IsString()) {
|
||||||
env->ThrowError("second argument is not a string");
|
env->ThrowError("second argument is not a string");
|
||||||
@ -88,49 +90,44 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
|
|||||||
|
|
||||||
Local<String> url = args[1].As<String>();
|
Local<String> url = args[1].As<String>();
|
||||||
|
|
||||||
Local<Module> mod;
|
Local<Module> module;
|
||||||
|
|
||||||
// compile
|
// compile
|
||||||
{
|
{
|
||||||
ScriptOrigin origin(url,
|
ScriptOrigin origin(url,
|
||||||
Integer::New(iso, 0),
|
Integer::New(isolate, 0), // line offset
|
||||||
Integer::New(iso, 0),
|
Integer::New(isolate, 0), // column offset
|
||||||
False(iso),
|
False(isolate), // is cross origin
|
||||||
Integer::New(iso, 0),
|
Local<Integer>(), // script id
|
||||||
FIXED_ONE_BYTE_STRING(iso, ""),
|
Local<Value>(), // source map URL
|
||||||
False(iso),
|
False(isolate), // is opaque (?)
|
||||||
False(iso),
|
False(isolate), // is WASM
|
||||||
True(iso));
|
True(isolate)); // is ES6 module
|
||||||
ScriptCompiler::Source source(source_text, origin);
|
ScriptCompiler::Source source(source_text, origin);
|
||||||
auto maybe_mod = ScriptCompiler::CompileModule(iso, &source);
|
if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module))
|
||||||
if (maybe_mod.IsEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mod = maybe_mod.ToLocalChecked();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto that = args.This();
|
|
||||||
auto ctx = that->CreationContext();
|
|
||||||
auto url_str = FIXED_ONE_BYTE_STRING(iso, "url");
|
|
||||||
|
|
||||||
if (!that->Set(ctx, url_str, url).FromMaybe(false)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModuleWrap* obj =
|
Local<Object> that = args.This();
|
||||||
new ModuleWrap(Environment::GetCurrent(ctx), that, mod, url);
|
Local<Context> context = that->CreationContext();
|
||||||
|
Local<String> url_str = FIXED_ONE_BYTE_STRING(isolate, "url");
|
||||||
|
|
||||||
env->module_map.emplace(mod->GetIdentityHash(), obj);
|
if (!that->Set(context, url_str, url).FromMaybe(false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleWrap* obj = new ModuleWrap(env, that, module, url);
|
||||||
|
|
||||||
|
env->module_map.emplace(module->GetIdentityHash(), obj);
|
||||||
Wrap(that, obj);
|
Wrap(that, obj);
|
||||||
|
|
||||||
that->SetIntegrityLevel(ctx, IntegrityLevel::kFrozen);
|
that->SetIntegrityLevel(context, IntegrityLevel::kFrozen);
|
||||||
args.GetReturnValue().Set(that);
|
args.GetReturnValue().Set(that);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
|
void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
Isolate* iso = args.GetIsolate();
|
Isolate* isolate = args.GetIsolate();
|
||||||
EscapableHandleScope handle_scope(iso);
|
|
||||||
if (!args[0]->IsFunction()) {
|
if (!args[0]->IsFunction()) {
|
||||||
env->ThrowError("first argument is not a function");
|
env->ThrowError("first argument is not a function");
|
||||||
return;
|
return;
|
||||||
@ -138,18 +135,19 @@ void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
|
|||||||
|
|
||||||
Local<Function> resolver_arg = args[0].As<Function>();
|
Local<Function> resolver_arg = args[0].As<Function>();
|
||||||
|
|
||||||
auto that = args.This();
|
Local<Object> that = args.This();
|
||||||
ModuleWrap* obj = Unwrap<ModuleWrap>(that);
|
ModuleWrap* obj = Unwrap<ModuleWrap>(that);
|
||||||
auto mod_context = that->CreationContext();
|
CHECK_NE(obj, nullptr);
|
||||||
|
Local<Context> mod_context = that->CreationContext();
|
||||||
if (obj->linked_) return;
|
if (obj->linked_) return;
|
||||||
obj->linked_ = true;
|
obj->linked_ = true;
|
||||||
Local<Module> mod(obj->module_.Get(iso));
|
Local<Module> module(obj->module_.Get(isolate));
|
||||||
|
|
||||||
// call the dependency resolve callbacks
|
// call the dependency resolve callbacks
|
||||||
for (int i = 0; i < mod->GetModuleRequestsLength(); i++) {
|
for (int i = 0; i < module->GetModuleRequestsLength(); i++) {
|
||||||
Local<String> specifier = mod->GetModuleRequest(i);
|
Local<String> specifier = module->GetModuleRequest(i);
|
||||||
Utf8Value specifier_utf(env->isolate(), specifier);
|
Utf8Value specifier_utf8(env->isolate(), specifier);
|
||||||
std::string specifier_std(*specifier_utf, specifier_utf.length());
|
std::string specifier_std(*specifier_utf8, specifier_utf8.length());
|
||||||
|
|
||||||
Local<Value> argv[] = {
|
Local<Value> argv[] = {
|
||||||
specifier
|
specifier
|
||||||
@ -169,17 +167,19 @@ void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
|
|||||||
obj->resolve_cache_[specifier_std].Reset(env->isolate(), resolve_promise);
|
obj->resolve_cache_[specifier_std].Reset(env->isolate(), resolve_promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
args.GetReturnValue().Set(handle_scope.Escape(that));
|
args.GetReturnValue().Set(that);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) {
|
void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) {
|
||||||
auto iso = args.GetIsolate();
|
Isolate* isolate = args.GetIsolate();
|
||||||
auto that = args.This();
|
Local<Object> that = args.This();
|
||||||
auto ctx = that->CreationContext();
|
Local<Context> context = that->CreationContext();
|
||||||
|
|
||||||
ModuleWrap* obj = Unwrap<ModuleWrap>(that);
|
ModuleWrap* obj = Unwrap<ModuleWrap>(that);
|
||||||
Local<Module> mod = obj->module_.Get(iso);
|
CHECK_NE(obj, nullptr);
|
||||||
Maybe<bool> ok = mod->InstantiateModule(ctx, ModuleWrap::ResolveCallback);
|
Local<Module> module = obj->module_.Get(isolate);
|
||||||
|
Maybe<bool> ok =
|
||||||
|
module->InstantiateModule(context, ModuleWrap::ResolveCallback);
|
||||||
|
|
||||||
// clear resolve cache on instantiate
|
// clear resolve cache on instantiate
|
||||||
for (auto& entry : obj->resolve_cache_)
|
for (auto& entry : obj->resolve_cache_)
|
||||||
@ -192,28 +192,28 @@ void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
|
void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
|
||||||
auto iso = args.GetIsolate();
|
Isolate* isolate = args.GetIsolate();
|
||||||
auto that = args.This();
|
Local<Object> that = args.This();
|
||||||
auto ctx = that->CreationContext();
|
Local<Context> context = that->CreationContext();
|
||||||
ModuleWrap* obj = Unwrap<ModuleWrap>(that);
|
ModuleWrap* obj = Unwrap<ModuleWrap>(that);
|
||||||
auto result = obj->module_.Get(iso)->Evaluate(ctx);
|
CHECK_NE(obj, nullptr);
|
||||||
|
MaybeLocal<Value> result = obj->module_.Get(isolate)->Evaluate(context);
|
||||||
|
|
||||||
if (result.IsEmpty()) {
|
if (result.IsEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ret = result.ToLocalChecked();
|
args.GetReturnValue().Set(result.ToLocalChecked());
|
||||||
args.GetReturnValue().Set(ret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModuleWrap::Namespace(const FunctionCallbackInfo<Value>& args) {
|
void ModuleWrap::Namespace(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
auto isolate = args.GetIsolate();
|
Isolate* isolate = args.GetIsolate();
|
||||||
auto that = args.This();
|
Local<Object> that = args.This();
|
||||||
ModuleWrap* obj = Unwrap<ModuleWrap>(that);
|
ModuleWrap* obj = Unwrap<ModuleWrap>(that);
|
||||||
CHECK_NE(obj, nullptr);
|
CHECK_NE(obj, nullptr);
|
||||||
|
|
||||||
auto module = obj->module_.Get(isolate);
|
Local<Module> module = obj->module_.Get(isolate);
|
||||||
|
|
||||||
switch (module->GetStatus()) {
|
switch (module->GetStatus()) {
|
||||||
default:
|
default:
|
||||||
@ -225,7 +225,7 @@ void ModuleWrap::Namespace(const FunctionCallbackInfo<Value>& args) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto result = module->GetModuleNamespace();
|
Local<Value> result = module->GetModuleNamespace();
|
||||||
args.GetReturnValue().Set(result);
|
args.GetReturnValue().Set(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +233,7 @@ MaybeLocal<Module> ModuleWrap::ResolveCallback(Local<Context> context,
|
|||||||
Local<String> specifier,
|
Local<String> specifier,
|
||||||
Local<Module> referrer) {
|
Local<Module> referrer) {
|
||||||
Environment* env = Environment::GetCurrent(context);
|
Environment* env = Environment::GetCurrent(context);
|
||||||
Isolate* iso = Isolate::GetCurrent();
|
Isolate* isolate = env->isolate();
|
||||||
if (env->module_map.count(referrer->GetIdentityHash()) == 0) {
|
if (env->module_map.count(referrer->GetIdentityHash()) == 0) {
|
||||||
env->ThrowError("linking error, unknown module");
|
env->ThrowError("linking error, unknown module");
|
||||||
return MaybeLocal<Module>();
|
return MaybeLocal<Module>();
|
||||||
@ -253,8 +253,8 @@ MaybeLocal<Module> ModuleWrap::ResolveCallback(Local<Context> context,
|
|||||||
return MaybeLocal<Module>();
|
return MaybeLocal<Module>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Utf8Value specifier_utf(env->isolate(), specifier);
|
Utf8Value specifier_utf8(env->isolate(), specifier);
|
||||||
std::string specifier_std(*specifier_utf, specifier_utf.length());
|
std::string specifier_std(*specifier_utf8, specifier_utf8.length());
|
||||||
|
|
||||||
if (dependent->resolve_cache_.count(specifier_std) != 1) {
|
if (dependent->resolve_cache_.count(specifier_std) != 1) {
|
||||||
env->ThrowError("linking error, not in local cache");
|
env->ThrowError("linking error, not in local cache");
|
||||||
@ -262,7 +262,7 @@ MaybeLocal<Module> ModuleWrap::ResolveCallback(Local<Context> context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Local<Promise> resolve_promise =
|
Local<Promise> resolve_promise =
|
||||||
dependent->resolve_cache_[specifier_std].Get(iso);
|
dependent->resolve_cache_[specifier_std].Get(isolate);
|
||||||
|
|
||||||
if (resolve_promise->State() != Promise::kFulfilled) {
|
if (resolve_promise->State() != Promise::kFulfilled) {
|
||||||
env->ThrowError("linking error, dependency promises must be resolved on "
|
env->ThrowError("linking error, dependency promises must be resolved on "
|
||||||
@ -270,176 +270,177 @@ MaybeLocal<Module> ModuleWrap::ResolveCallback(Local<Context> context,
|
|||||||
return MaybeLocal<Module>();
|
return MaybeLocal<Module>();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto module_object = resolve_promise->Result().As<Object>();
|
Local<Object> module_object = resolve_promise->Result().As<Object>();
|
||||||
if (module_object.IsEmpty() || !module_object->IsObject()) {
|
if (module_object.IsEmpty() || !module_object->IsObject()) {
|
||||||
env->ThrowError("linking error, expected a valid module object from "
|
env->ThrowError("linking error, expected a valid module object from "
|
||||||
"resolver");
|
"resolver");
|
||||||
return MaybeLocal<Module>();
|
return MaybeLocal<Module>();
|
||||||
}
|
}
|
||||||
|
|
||||||
ModuleWrap* mod;
|
ModuleWrap* module;
|
||||||
ASSIGN_OR_RETURN_UNWRAP(&mod, module_object, MaybeLocal<Module>());
|
ASSIGN_OR_RETURN_UNWRAP(&module, module_object, MaybeLocal<Module>());
|
||||||
return mod->module_.Get(env->isolate());
|
return module->module_.Get(env->isolate());
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
URL __init_cwd() {
|
// Tests whether a path starts with /, ./ or ../
|
||||||
std::string specifier = "file://";
|
// In WhatWG terminology, the alternative case is called a "bare" specifier
|
||||||
#ifdef _WIN32
|
// (e.g. in `import "jquery"`).
|
||||||
// MAX_PATH is in characters, not bytes. Make sure we have enough headroom.
|
inline bool ShouldBeTreatedAsRelativeOrAbsolutePath(
|
||||||
char buf[MAX_PATH * 4];
|
const std::string& specifier) {
|
||||||
#else
|
size_t len = specifier.length();
|
||||||
char buf[PATH_MAX];
|
if (len == 0)
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t cwd_len = sizeof(buf);
|
|
||||||
int err = uv_cwd(buf, &cwd_len);
|
|
||||||
if (err) {
|
|
||||||
return URL("");
|
|
||||||
}
|
|
||||||
specifier += buf;
|
|
||||||
specifier += "/";
|
|
||||||
return URL(specifier);
|
|
||||||
}
|
|
||||||
static URL INITIAL_CWD(__init_cwd());
|
|
||||||
inline bool is_relative_or_absolute_path(std::string specifier) {
|
|
||||||
auto len = specifier.length();
|
|
||||||
if (len <= 0) {
|
|
||||||
return false;
|
return false;
|
||||||
} else if (specifier[0] == '/') {
|
if (specifier[0] == '/') {
|
||||||
return true;
|
return true;
|
||||||
} else if (specifier[0] == '.') {
|
} else if (specifier[0] == '.') {
|
||||||
if (len == 1 || specifier[1] == '/') {
|
if (len == 1 || specifier[1] == '/')
|
||||||
return true;
|
return true;
|
||||||
} else if (specifier[1] == '.') {
|
if (specifier[1] == '.') {
|
||||||
if (len == 2 || specifier[2] == '/') {
|
if (len == 2 || specifier[2] == '/')
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
struct read_result {
|
|
||||||
bool had_error = false;
|
std::string ReadFile(uv_file file) {
|
||||||
std::string source;
|
std::string contents;
|
||||||
} read_result;
|
|
||||||
inline const struct read_result read_file(uv_file file) {
|
|
||||||
struct read_result ret;
|
|
||||||
std::string src;
|
|
||||||
uv_fs_t req;
|
uv_fs_t req;
|
||||||
void* base = malloc(4096);
|
char buffer_memory[4096];
|
||||||
if (base == nullptr) {
|
uv_buf_t buf = uv_buf_init(buffer_memory, sizeof(buffer_memory));
|
||||||
ret.had_error = true;
|
do {
|
||||||
return ret;
|
uv_fs_read(uv_default_loop(),
|
||||||
|
&req,
|
||||||
|
file,
|
||||||
|
&buf,
|
||||||
|
1,
|
||||||
|
contents.length(), // offset
|
||||||
|
nullptr);
|
||||||
|
if (req.result <= 0)
|
||||||
|
break;
|
||||||
|
contents.append(buf.base, req.result);
|
||||||
|
} while (true);
|
||||||
|
return contents;
|
||||||
}
|
}
|
||||||
uv_buf_t buf = uv_buf_init(static_cast<char*>(base), 4096);
|
|
||||||
uv_fs_read(uv_default_loop(), &req, file, &buf, 1, 0, nullptr);
|
enum CheckFileOptions {
|
||||||
while (req.result > 0) {
|
LEAVE_OPEN_AFTER_CHECK,
|
||||||
src += std::string(static_cast<const char*>(buf.base), req.result);
|
CLOSE_AFTER_CHECK
|
||||||
uv_fs_read(uv_default_loop(), &req, file, &buf, 1, src.length(), nullptr);
|
|
||||||
}
|
|
||||||
ret.source = src;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
struct file_check {
|
|
||||||
bool failed = true;
|
|
||||||
uv_file file = -1;
|
|
||||||
};
|
};
|
||||||
inline const struct file_check check_file(const URL& search,
|
|
||||||
bool close = false,
|
Maybe<uv_file> CheckFile(const URL& search,
|
||||||
bool allow_dir = false) {
|
CheckFileOptions opt = CLOSE_AFTER_CHECK) {
|
||||||
struct file_check ret;
|
|
||||||
uv_fs_t fs_req;
|
uv_fs_t fs_req;
|
||||||
std::string path = search.ToFilePath();
|
std::string path = search.ToFilePath();
|
||||||
if (path.empty()) {
|
if (path.empty()) {
|
||||||
return ret;
|
return Nothing<uv_file>();
|
||||||
}
|
}
|
||||||
uv_fs_open(nullptr, &fs_req, path.c_str(), O_RDONLY, 0, nullptr);
|
uv_fs_open(nullptr, &fs_req, path.c_str(), O_RDONLY, 0, nullptr);
|
||||||
auto fd = fs_req.result;
|
uv_file fd = fs_req.result;
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
return ret;
|
return Nothing<uv_file>();
|
||||||
}
|
}
|
||||||
if (!allow_dir) {
|
|
||||||
uv_fs_fstat(nullptr, &fs_req, fd, nullptr);
|
uv_fs_fstat(nullptr, &fs_req, fd, nullptr);
|
||||||
if (fs_req.statbuf.st_mode & S_IFDIR) {
|
if (fs_req.statbuf.st_mode & S_IFDIR) {
|
||||||
uv_fs_close(nullptr, &fs_req, fd, nullptr);
|
uv_fs_close(nullptr, &fs_req, fd, nullptr);
|
||||||
return ret;
|
return Nothing<uv_file>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opt == CLOSE_AFTER_CHECK)
|
||||||
|
uv_fs_close(nullptr, &fs_req, fd, nullptr);
|
||||||
|
return Just(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ResolveExtensionsOptions {
|
||||||
|
TRY_EXACT_NAME,
|
||||||
|
ONLY_VIA_EXTENSIONS
|
||||||
|
};
|
||||||
|
|
||||||
|
template<ResolveExtensionsOptions options>
|
||||||
|
Maybe<URL> ResolveExtensions(const URL& search) {
|
||||||
|
if (options == TRY_EXACT_NAME) {
|
||||||
|
Maybe<uv_file> check = CheckFile(search);
|
||||||
|
if (!check.IsNothing()) {
|
||||||
|
return Just(search);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret.failed = false;
|
|
||||||
ret.file = fd;
|
for (const char* extension : EXTENSIONS) {
|
||||||
if (close) uv_fs_close(nullptr, &fs_req, fd, nullptr);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
URL resolve_extensions(const URL& search, bool check_exact = true) {
|
|
||||||
if (check_exact) {
|
|
||||||
auto check = check_file(search, true);
|
|
||||||
if (!check.failed) {
|
|
||||||
return search;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto extension : EXTENSIONS) {
|
|
||||||
URL guess(search.path() + extension, &search);
|
URL guess(search.path() + extension, &search);
|
||||||
auto check = check_file(guess, true);
|
Maybe<uv_file> check = CheckFile(guess);
|
||||||
if (!check.failed) {
|
if (!check.IsNothing()) {
|
||||||
return guess;
|
return Just(guess);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return URL("");
|
|
||||||
|
return Nothing<URL>();
|
||||||
}
|
}
|
||||||
inline URL resolve_index(const URL& search) {
|
|
||||||
return resolve_extensions(URL("index", &search), false);
|
inline Maybe<URL> ResolveIndex(const URL& search) {
|
||||||
|
return ResolveExtensions<ONLY_VIA_EXTENSIONS>(URL("index", search));
|
||||||
}
|
}
|
||||||
URL resolve_main(const URL& search) {
|
|
||||||
|
Maybe<URL> ResolveMain(Environment* env, const URL& search) {
|
||||||
URL pkg("package.json", &search);
|
URL pkg("package.json", &search);
|
||||||
auto check = check_file(pkg);
|
Maybe<uv_file> check = CheckFile(pkg, LEAVE_OPEN_AFTER_CHECK);
|
||||||
if (!check.failed) {
|
if (check.IsNothing()) {
|
||||||
auto iso = Isolate::GetCurrent();
|
return Nothing<URL>();
|
||||||
auto ctx = iso->GetCurrentContext();
|
}
|
||||||
auto read = read_file(check.file);
|
|
||||||
|
Isolate* isolate = env->isolate();
|
||||||
|
Local<Context> context = isolate->GetCurrentContext();
|
||||||
|
std::string pkg_src = ReadFile(check.FromJust());
|
||||||
uv_fs_t fs_req;
|
uv_fs_t fs_req;
|
||||||
// if we fail to close :-/
|
uv_fs_close(nullptr, &fs_req, check.FromJust(), nullptr);
|
||||||
uv_fs_close(nullptr, &fs_req, check.file, nullptr);
|
|
||||||
if (read.had_error) return URL("");
|
// It's not okay for the called of this method to not be able to tell
|
||||||
std::string pkg_src = read.source;
|
// whether an exception is pending or not.
|
||||||
Local<String> src =
|
TryCatch try_catch(isolate);
|
||||||
String::NewFromUtf8(iso, pkg_src.c_str(),
|
|
||||||
String::kNormalString, pkg_src.length());
|
Local<String> src;
|
||||||
if (src.IsEmpty()) return URL("");
|
if (!String::NewFromUtf8(isolate,
|
||||||
auto maybe_pkg_json = JSON::Parse(ctx, src);
|
pkg_src.c_str(),
|
||||||
if (maybe_pkg_json.IsEmpty()) return URL("");
|
v8::NewStringType::kNormal,
|
||||||
auto pkg_json_obj = maybe_pkg_json.ToLocalChecked().As<Object>();
|
pkg_src.length()).ToLocal(&src)) {
|
||||||
if (!pkg_json_obj->IsObject()) return URL("");
|
return Nothing<URL>();
|
||||||
auto maybe_pkg_main = pkg_json_obj->Get(
|
}
|
||||||
ctx, FIXED_ONE_BYTE_STRING(iso, "main"));
|
|
||||||
if (maybe_pkg_main.IsEmpty()) return URL("");
|
Local<Value> pkg_json;
|
||||||
auto pkg_main_str = maybe_pkg_main.ToLocalChecked().As<String>();
|
if (!JSON::Parse(context, src).ToLocal(&pkg_json) || !pkg_json->IsObject())
|
||||||
if (!pkg_main_str->IsString()) return URL("");
|
return Nothing<URL>();
|
||||||
Utf8Value main_utf8(iso, pkg_main_str);
|
Local<Value> pkg_main;
|
||||||
|
if (!pkg_json.As<Object>()->Get(context, env->main_string())
|
||||||
|
.ToLocal(&pkg_main) || !pkg_main->IsString()) {
|
||||||
|
return Nothing<URL>();
|
||||||
|
}
|
||||||
|
Utf8Value main_utf8(isolate, pkg_main.As<String>());
|
||||||
std::string main_std(*main_utf8, main_utf8.length());
|
std::string main_std(*main_utf8, main_utf8.length());
|
||||||
if (!is_relative_or_absolute_path(main_std)) {
|
if (!ShouldBeTreatedAsRelativeOrAbsolutePath(main_std)) {
|
||||||
main_std.insert(0, "./");
|
main_std.insert(0, "./");
|
||||||
}
|
}
|
||||||
return Resolve(main_std, &search);
|
return Resolve(env, main_std, search);
|
||||||
}
|
}
|
||||||
return URL("");
|
|
||||||
}
|
Maybe<URL> ResolveModule(Environment* env,
|
||||||
URL resolve_module(std::string specifier, const URL* base) {
|
const std::string& specifier,
|
||||||
|
const URL& base) {
|
||||||
URL parent(".", base);
|
URL parent(".", base);
|
||||||
URL dir("");
|
URL dir("");
|
||||||
do {
|
do {
|
||||||
dir = parent;
|
dir = parent;
|
||||||
auto check = Resolve("./node_modules/" + specifier, &dir, true);
|
Maybe<URL> check = Resolve(env, "./node_modules/" + specifier, dir, true);
|
||||||
if (!(check.flags() & URL_FLAGS_FAILED)) {
|
if (!check.IsNothing()) {
|
||||||
const auto limit = specifier.find('/');
|
const size_t limit = specifier.find('/');
|
||||||
const auto spec_len = limit == std::string::npos ?
|
const size_t spec_len =
|
||||||
specifier.length() :
|
limit == std::string::npos ? specifier.length() :
|
||||||
limit + 1;
|
limit + 1;
|
||||||
std::string chroot =
|
std::string chroot =
|
||||||
dir.path() + "node_modules/" + specifier.substr(0, spec_len);
|
dir.path() + "node_modules/" + specifier.substr(0, spec_len);
|
||||||
if (check.path().substr(0, chroot.length()) != chroot) {
|
if (check.FromJust().path().substr(0, chroot.length()) != chroot) {
|
||||||
return URL("");
|
return Nothing<URL>();
|
||||||
}
|
}
|
||||||
return check;
|
return check;
|
||||||
} else {
|
} else {
|
||||||
@ -447,45 +448,51 @@ URL resolve_module(std::string specifier, const URL* base) {
|
|||||||
}
|
}
|
||||||
parent = URL("..", &dir);
|
parent = URL("..", &dir);
|
||||||
} while (parent.path() != dir.path());
|
} while (parent.path() != dir.path());
|
||||||
return URL("");
|
return Nothing<URL>();
|
||||||
}
|
}
|
||||||
|
|
||||||
URL resolve_directory(const URL& search, bool read_pkg_json) {
|
Maybe<URL> ResolveDirectory(Environment* env,
|
||||||
|
const URL& search,
|
||||||
|
bool read_pkg_json) {
|
||||||
if (read_pkg_json) {
|
if (read_pkg_json) {
|
||||||
auto main = resolve_main(search);
|
Maybe<URL> main = ResolveMain(env, search);
|
||||||
if (!(main.flags() & URL_FLAGS_FAILED)) return main;
|
if (!main.IsNothing())
|
||||||
|
return main;
|
||||||
}
|
}
|
||||||
return resolve_index(search);
|
return ResolveIndex(search);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
URL Resolve(std::string specifier, const URL* base, bool read_pkg_json) {
|
Maybe<URL> Resolve(Environment* env,
|
||||||
|
const std::string& specifier,
|
||||||
|
const URL& base,
|
||||||
|
bool read_pkg_json) {
|
||||||
URL pure_url(specifier);
|
URL pure_url(specifier);
|
||||||
if (!(pure_url.flags() & URL_FLAGS_FAILED)) {
|
if (!(pure_url.flags() & URL_FLAGS_FAILED)) {
|
||||||
// just check existence, without altering
|
// just check existence, without altering
|
||||||
auto check = check_file(pure_url, true);
|
Maybe<uv_file> check = CheckFile(pure_url);
|
||||||
if (check.failed) {
|
if (check.IsNothing()) {
|
||||||
return URL("");
|
return Nothing<URL>();
|
||||||
}
|
}
|
||||||
return pure_url;
|
return Just(pure_url);
|
||||||
}
|
}
|
||||||
if (specifier.length() == 0) {
|
if (specifier.length() == 0) {
|
||||||
return URL("");
|
return Nothing<URL>();
|
||||||
}
|
}
|
||||||
if (is_relative_or_absolute_path(specifier)) {
|
if (ShouldBeTreatedAsRelativeOrAbsolutePath(specifier)) {
|
||||||
URL resolved(specifier, base);
|
URL resolved(specifier, base);
|
||||||
auto file = resolve_extensions(resolved);
|
Maybe<URL> file = ResolveExtensions<TRY_EXACT_NAME>(resolved);
|
||||||
if (!(file.flags() & URL_FLAGS_FAILED)) return file;
|
if (!file.IsNothing())
|
||||||
|
return file;
|
||||||
if (specifier.back() != '/') {
|
if (specifier.back() != '/') {
|
||||||
resolved = URL(specifier + "/", base);
|
resolved = URL(specifier + "/", base);
|
||||||
}
|
}
|
||||||
return resolve_directory(resolved, read_pkg_json);
|
return ResolveDirectory(env, resolved, read_pkg_json);
|
||||||
} else {
|
} else {
|
||||||
return resolve_module(specifier, base);
|
return ResolveModule(env, specifier, base);
|
||||||
}
|
}
|
||||||
UNREACHABLE();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
|
void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
|
||||||
@ -504,29 +511,29 @@ void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
|
|||||||
env->ThrowError("first argument is not a string");
|
env->ThrowError("first argument is not a string");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Utf8Value specifier_utf(env->isolate(), args[0]);
|
Utf8Value specifier_utf8(env->isolate(), args[0]);
|
||||||
|
std::string specifier_std(*specifier_utf8, specifier_utf8.length());
|
||||||
|
|
||||||
if (!args[1]->IsString()) {
|
if (!args[1]->IsString()) {
|
||||||
env->ThrowError("second argument is not a string");
|
env->ThrowError("second argument is not a string");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Utf8Value url_utf(env->isolate(), args[1]);
|
Utf8Value url_utf8(env->isolate(), args[1]);
|
||||||
URL url(*url_utf, url_utf.length());
|
URL url(*url_utf8, url_utf8.length());
|
||||||
|
|
||||||
if (url.flags() & URL_FLAGS_FAILED) {
|
if (url.flags() & URL_FLAGS_FAILED) {
|
||||||
env->ThrowError("second argument is not a URL string");
|
env->ThrowError("second argument is not a URL string");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
URL result = node::loader::Resolve(*specifier_utf, &url, true);
|
Maybe<URL> result = node::loader::Resolve(env, specifier_std, url, true);
|
||||||
if (result.flags() & URL_FLAGS_FAILED) {
|
if (result.IsNothing() || (result.FromJust().flags() & URL_FLAGS_FAILED)) {
|
||||||
std::string msg = "Cannot find module ";
|
std::string msg = "Cannot find module " + specifier_std;
|
||||||
msg += *specifier_utf;
|
|
||||||
env->ThrowError(msg.c_str());
|
env->ThrowError(msg.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
args.GetReturnValue().Set(result.ToObject(env));
|
args.GetReturnValue().Set(result.FromJust().ToObject(env));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModuleWrap::Initialize(Local<Object> target,
|
void ModuleWrap::Initialize(Local<Object> target,
|
||||||
|
@ -12,7 +12,9 @@
|
|||||||
namespace node {
|
namespace node {
|
||||||
namespace loader {
|
namespace loader {
|
||||||
|
|
||||||
node::url::URL Resolve(std::string specifier, const node::url::URL* base,
|
v8::Maybe<url::URL> Resolve(Environment* env,
|
||||||
|
const std::string& specifier,
|
||||||
|
const url::URL& base,
|
||||||
bool read_pkg_json = false);
|
bool read_pkg_json = false);
|
||||||
|
|
||||||
class ModuleWrap : public BaseObject {
|
class ModuleWrap : public BaseObject {
|
||||||
@ -23,7 +25,7 @@ class ModuleWrap : public BaseObject {
|
|||||||
v8::Local<v8::Context> context);
|
v8::Local<v8::Context> context);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ModuleWrap(node::Environment* env,
|
ModuleWrap(Environment* env,
|
||||||
v8::Local<v8::Object> object,
|
v8::Local<v8::Object> object,
|
||||||
v8::Local<v8::Module> module,
|
v8::Local<v8::Module> module,
|
||||||
v8::Local<v8::String> url);
|
v8::Local<v8::String> url);
|
||||||
|
@ -118,6 +118,9 @@ class URL {
|
|||||||
URL(std::string input, const URL* base) :
|
URL(std::string input, const URL* base) :
|
||||||
URL(input.c_str(), input.length(), base) {}
|
URL(input.c_str(), input.length(), base) {}
|
||||||
|
|
||||||
|
URL(std::string input, const URL& base) :
|
||||||
|
URL(input.c_str(), input.length(), &base) {}
|
||||||
|
|
||||||
URL(std::string input, std::string base) :
|
URL(std::string input, std::string base) :
|
||||||
URL(input.c_str(), input.length(), base.c_str(), base.length()) {}
|
URL(input.c_str(), input.length(), base.c_str(), base.length()) {}
|
||||||
|
|
||||||
@ -168,6 +171,13 @@ class URL {
|
|||||||
|
|
||||||
const Local<Value> ToObject(Environment* env) const;
|
const Local<Value> ToObject(Environment* env) const;
|
||||||
|
|
||||||
|
URL(const URL&) = default;
|
||||||
|
URL& operator=(const URL&) = default;
|
||||||
|
URL(URL&&) = default;
|
||||||
|
URL& operator=(URL&&) = default;
|
||||||
|
|
||||||
|
URL() : URL("") {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct url_data context_;
|
struct url_data context_;
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user