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:
Anna Henningsen 2017-10-27 01:43:51 +02:00
parent 89b3228f4d
commit e22a8f17a5
No known key found for this signature in database
GPG Key ID: 9C63F3A6CD2AD8F9
4 changed files with 227 additions and 207 deletions

View File

@ -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") \

View File

@ -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,

View File

@ -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);

View File

@ -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_;
}; };