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(length_string, "length") \
V(mac_string, "mac") \
V(main_string, "main") \
V(max_buffer_string, "maxBuffer") \
V(message_string, "message") \
V(minttl_string, "minttl") \

View File

@ -14,7 +14,6 @@ namespace loader {
using node::url::URL;
using node::url::URL_FLAGS_FAILED;
using v8::Context;
using v8::EscapableHandleScope;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
@ -23,15 +22,18 @@ using v8::Integer;
using v8::IntegrityLevel;
using v8::Isolate;
using v8::JSON;
using v8::Just;
using v8::Local;
using v8::Maybe;
using v8::MaybeLocal;
using v8::Module;
using v8::Nothing;
using v8::Object;
using v8::Promise;
using v8::ScriptCompiler;
using v8::ScriptOrigin;
using v8::String;
using v8::TryCatch;
using v8::Value;
static const char* const EXTENSIONS[] = {".mjs", ".js", ".json", ".node"};
@ -61,7 +63,7 @@ ModuleWrap::~ModuleWrap() {
void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* iso = args.GetIsolate();
Isolate* isolate = args.GetIsolate();
if (!args.IsConstructCall()) {
env->ThrowError("constructor must be called using new");
@ -79,7 +81,7 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
return;
}
auto source_text = args[0].As<String>();
Local<String> source_text = args[0].As<String>();
if (!args[1]->IsString()) {
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<Module> mod;
Local<Module> module;
// compile
{
ScriptOrigin origin(url,
Integer::New(iso, 0),
Integer::New(iso, 0),
False(iso),
Integer::New(iso, 0),
FIXED_ONE_BYTE_STRING(iso, ""),
False(iso),
False(iso),
True(iso));
Integer::New(isolate, 0), // line offset
Integer::New(isolate, 0), // column offset
False(isolate), // is cross origin
Local<Integer>(), // script id
Local<Value>(), // source map URL
False(isolate), // is opaque (?)
False(isolate), // is WASM
True(isolate)); // is ES6 module
ScriptCompiler::Source source(source_text, origin);
auto maybe_mod = ScriptCompiler::CompileModule(iso, &source);
if (maybe_mod.IsEmpty()) {
return;
}
mod = maybe_mod.ToLocalChecked();
if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module))
return;
}
auto that = args.This();
auto ctx = that->CreationContext();
auto url_str = FIXED_ONE_BYTE_STRING(iso, "url");
Local<Object> that = args.This();
Local<Context> context = that->CreationContext();
Local<String> url_str = FIXED_ONE_BYTE_STRING(isolate, "url");
if (!that->Set(ctx, url_str, url).FromMaybe(false)) {
if (!that->Set(context, url_str, url).FromMaybe(false)) {
return;
}
ModuleWrap* obj =
new ModuleWrap(Environment::GetCurrent(ctx), that, mod, url);
ModuleWrap* obj = new ModuleWrap(env, that, module, url);
env->module_map.emplace(mod->GetIdentityHash(), obj);
env->module_map.emplace(module->GetIdentityHash(), obj);
Wrap(that, obj);
that->SetIntegrityLevel(ctx, IntegrityLevel::kFrozen);
that->SetIntegrityLevel(context, IntegrityLevel::kFrozen);
args.GetReturnValue().Set(that);
}
void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* iso = args.GetIsolate();
EscapableHandleScope handle_scope(iso);
Isolate* isolate = args.GetIsolate();
if (!args[0]->IsFunction()) {
env->ThrowError("first argument is not a function");
return;
@ -138,18 +135,19 @@ void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
Local<Function> resolver_arg = args[0].As<Function>();
auto that = args.This();
Local<Object> that = args.This();
ModuleWrap* obj = Unwrap<ModuleWrap>(that);
auto mod_context = that->CreationContext();
CHECK_NE(obj, nullptr);
Local<Context> mod_context = that->CreationContext();
if (obj->linked_) return;
obj->linked_ = true;
Local<Module> mod(obj->module_.Get(iso));
Local<Module> module(obj->module_.Get(isolate));
// call the dependency resolve callbacks
for (int i = 0; i < mod->GetModuleRequestsLength(); i++) {
Local<String> specifier = mod->GetModuleRequest(i);
Utf8Value specifier_utf(env->isolate(), specifier);
std::string specifier_std(*specifier_utf, specifier_utf.length());
for (int i = 0; i < module->GetModuleRequestsLength(); i++) {
Local<String> specifier = module->GetModuleRequest(i);
Utf8Value specifier_utf8(env->isolate(), specifier);
std::string specifier_std(*specifier_utf8, specifier_utf8.length());
Local<Value> argv[] = {
specifier
@ -169,17 +167,19 @@ void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
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) {
auto iso = args.GetIsolate();
auto that = args.This();
auto ctx = that->CreationContext();
Isolate* isolate = args.GetIsolate();
Local<Object> that = args.This();
Local<Context> context = that->CreationContext();
ModuleWrap* obj = Unwrap<ModuleWrap>(that);
Local<Module> mod = obj->module_.Get(iso);
Maybe<bool> ok = mod->InstantiateModule(ctx, ModuleWrap::ResolveCallback);
CHECK_NE(obj, nullptr);
Local<Module> module = obj->module_.Get(isolate);
Maybe<bool> ok =
module->InstantiateModule(context, ModuleWrap::ResolveCallback);
// clear resolve cache on instantiate
for (auto& entry : obj->resolve_cache_)
@ -192,28 +192,28 @@ void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) {
}
void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
auto iso = args.GetIsolate();
auto that = args.This();
auto ctx = that->CreationContext();
Isolate* isolate = args.GetIsolate();
Local<Object> that = args.This();
Local<Context> context = that->CreationContext();
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()) {
return;
}
auto ret = result.ToLocalChecked();
args.GetReturnValue().Set(ret);
args.GetReturnValue().Set(result.ToLocalChecked());
}
void ModuleWrap::Namespace(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
auto isolate = args.GetIsolate();
auto that = args.This();
Isolate* isolate = args.GetIsolate();
Local<Object> that = args.This();
ModuleWrap* obj = Unwrap<ModuleWrap>(that);
CHECK_NE(obj, nullptr);
auto module = obj->module_.Get(isolate);
Local<Module> module = obj->module_.Get(isolate);
switch (module->GetStatus()) {
default:
@ -225,7 +225,7 @@ void ModuleWrap::Namespace(const FunctionCallbackInfo<Value>& args) {
break;
}
auto result = module->GetModuleNamespace();
Local<Value> result = module->GetModuleNamespace();
args.GetReturnValue().Set(result);
}
@ -233,7 +233,7 @@ MaybeLocal<Module> ModuleWrap::ResolveCallback(Local<Context> context,
Local<String> specifier,
Local<Module> referrer) {
Environment* env = Environment::GetCurrent(context);
Isolate* iso = Isolate::GetCurrent();
Isolate* isolate = env->isolate();
if (env->module_map.count(referrer->GetIdentityHash()) == 0) {
env->ThrowError("linking error, unknown module");
return MaybeLocal<Module>();
@ -253,8 +253,8 @@ MaybeLocal<Module> ModuleWrap::ResolveCallback(Local<Context> context,
return MaybeLocal<Module>();
}
Utf8Value specifier_utf(env->isolate(), specifier);
std::string specifier_std(*specifier_utf, specifier_utf.length());
Utf8Value specifier_utf8(env->isolate(), specifier);
std::string specifier_std(*specifier_utf8, specifier_utf8.length());
if (dependent->resolve_cache_.count(specifier_std) != 1) {
env->ThrowError("linking error, not in local cache");
@ -262,7 +262,7 @@ MaybeLocal<Module> ModuleWrap::ResolveCallback(Local<Context> context,
}
Local<Promise> resolve_promise =
dependent->resolve_cache_[specifier_std].Get(iso);
dependent->resolve_cache_[specifier_std].Get(isolate);
if (resolve_promise->State() != Promise::kFulfilled) {
env->ThrowError("linking error, dependency promises must be resolved on "
@ -270,176 +270,177 @@ MaybeLocal<Module> ModuleWrap::ResolveCallback(Local<Context> context,
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()) {
env->ThrowError("linking error, expected a valid module object from "
"resolver");
return MaybeLocal<Module>();
}
ModuleWrap* mod;
ASSIGN_OR_RETURN_UNWRAP(&mod, module_object, MaybeLocal<Module>());
return mod->module_.Get(env->isolate());
ModuleWrap* module;
ASSIGN_OR_RETURN_UNWRAP(&module, module_object, MaybeLocal<Module>());
return module->module_.Get(env->isolate());
}
namespace {
URL __init_cwd() {
std::string specifier = "file://";
#ifdef _WIN32
// MAX_PATH is in characters, not bytes. Make sure we have enough headroom.
char buf[MAX_PATH * 4];
#else
char buf[PATH_MAX];
#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) {
// Tests whether a path starts with /, ./ or ../
// In WhatWG terminology, the alternative case is called a "bare" specifier
// (e.g. in `import "jquery"`).
inline bool ShouldBeTreatedAsRelativeOrAbsolutePath(
const std::string& specifier) {
size_t len = specifier.length();
if (len == 0)
return false;
} else if (specifier[0] == '/') {
if (specifier[0] == '/') {
return true;
} else if (specifier[0] == '.') {
if (len == 1 || specifier[1] == '/') {
if (len == 1 || specifier[1] == '/')
return true;
} else if (specifier[1] == '.') {
if (len == 2 || specifier[2] == '/') {
if (specifier[1] == '.') {
if (len == 2 || specifier[2] == '/')
return true;
}
}
}
return false;
}
struct read_result {
bool had_error = false;
std::string source;
} read_result;
inline const struct read_result read_file(uv_file file) {
struct read_result ret;
std::string src;
std::string ReadFile(uv_file file) {
std::string contents;
uv_fs_t req;
void* base = malloc(4096);
if (base == nullptr) {
ret.had_error = true;
return ret;
}
uv_buf_t buf = uv_buf_init(static_cast<char*>(base), 4096);
uv_fs_read(uv_default_loop(), &req, file, &buf, 1, 0, nullptr);
while (req.result > 0) {
src += std::string(static_cast<const char*>(buf.base), req.result);
uv_fs_read(uv_default_loop(), &req, file, &buf, 1, src.length(), nullptr);
}
ret.source = src;
return ret;
char buffer_memory[4096];
uv_buf_t buf = uv_buf_init(buffer_memory, sizeof(buffer_memory));
do {
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;
}
struct file_check {
bool failed = true;
uv_file file = -1;
enum CheckFileOptions {
LEAVE_OPEN_AFTER_CHECK,
CLOSE_AFTER_CHECK
};
inline const struct file_check check_file(const URL& search,
bool close = false,
bool allow_dir = false) {
struct file_check ret;
Maybe<uv_file> CheckFile(const URL& search,
CheckFileOptions opt = CLOSE_AFTER_CHECK) {
uv_fs_t fs_req;
std::string path = search.ToFilePath();
if (path.empty()) {
return ret;
return Nothing<uv_file>();
}
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) {
return ret;
return Nothing<uv_file>();
}
if (!allow_dir) {
uv_fs_fstat(nullptr, &fs_req, fd, nullptr);
if (fs_req.statbuf.st_mode & S_IFDIR) {
uv_fs_close(nullptr, &fs_req, fd, nullptr);
return ret;
}
uv_fs_fstat(nullptr, &fs_req, fd, nullptr);
if (fs_req.statbuf.st_mode & S_IFDIR) {
uv_fs_close(nullptr, &fs_req, fd, nullptr);
return Nothing<uv_file>();
}
ret.failed = false;
ret.file = fd;
if (close) uv_fs_close(nullptr, &fs_req, fd, nullptr);
return ret;
if (opt == CLOSE_AFTER_CHECK)
uv_fs_close(nullptr, &fs_req, fd, nullptr);
return Just(fd);
}
URL resolve_extensions(const URL& search, bool check_exact = true) {
if (check_exact) {
auto check = check_file(search, true);
if (!check.failed) {
return search;
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);
}
}
for (auto extension : EXTENSIONS) {
for (const char* extension : EXTENSIONS) {
URL guess(search.path() + extension, &search);
auto check = check_file(guess, true);
if (!check.failed) {
return guess;
Maybe<uv_file> check = CheckFile(guess);
if (!check.IsNothing()) {
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);
auto check = check_file(pkg);
if (!check.failed) {
auto iso = Isolate::GetCurrent();
auto ctx = iso->GetCurrentContext();
auto read = read_file(check.file);
uv_fs_t fs_req;
// if we fail to close :-/
uv_fs_close(nullptr, &fs_req, check.file, nullptr);
if (read.had_error) return URL("");
std::string pkg_src = read.source;
Local<String> src =
String::NewFromUtf8(iso, pkg_src.c_str(),
String::kNormalString, pkg_src.length());
if (src.IsEmpty()) return URL("");
auto maybe_pkg_json = JSON::Parse(ctx, src);
if (maybe_pkg_json.IsEmpty()) return URL("");
auto pkg_json_obj = maybe_pkg_json.ToLocalChecked().As<Object>();
if (!pkg_json_obj->IsObject()) return URL("");
auto maybe_pkg_main = pkg_json_obj->Get(
ctx, FIXED_ONE_BYTE_STRING(iso, "main"));
if (maybe_pkg_main.IsEmpty()) return URL("");
auto pkg_main_str = maybe_pkg_main.ToLocalChecked().As<String>();
if (!pkg_main_str->IsString()) return URL("");
Utf8Value main_utf8(iso, pkg_main_str);
std::string main_std(*main_utf8, main_utf8.length());
if (!is_relative_or_absolute_path(main_std)) {
main_std.insert(0, "./");
}
return Resolve(main_std, &search);
Maybe<uv_file> check = CheckFile(pkg, LEAVE_OPEN_AFTER_CHECK);
if (check.IsNothing()) {
return Nothing<URL>();
}
return URL("");
Isolate* isolate = env->isolate();
Local<Context> context = isolate->GetCurrentContext();
std::string pkg_src = ReadFile(check.FromJust());
uv_fs_t fs_req;
uv_fs_close(nullptr, &fs_req, check.FromJust(), nullptr);
// It's not okay for the called of this method to not be able to tell
// whether an exception is pending or not.
TryCatch try_catch(isolate);
Local<String> src;
if (!String::NewFromUtf8(isolate,
pkg_src.c_str(),
v8::NewStringType::kNormal,
pkg_src.length()).ToLocal(&src)) {
return Nothing<URL>();
}
Local<Value> pkg_json;
if (!JSON::Parse(context, src).ToLocal(&pkg_json) || !pkg_json->IsObject())
return Nothing<URL>();
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());
if (!ShouldBeTreatedAsRelativeOrAbsolutePath(main_std)) {
main_std.insert(0, "./");
}
return Resolve(env, main_std, search);
}
URL resolve_module(std::string specifier, const URL* base) {
Maybe<URL> ResolveModule(Environment* env,
const std::string& specifier,
const URL& base) {
URL parent(".", base);
URL dir("");
do {
dir = parent;
auto check = Resolve("./node_modules/" + specifier, &dir, true);
if (!(check.flags() & URL_FLAGS_FAILED)) {
const auto limit = specifier.find('/');
const auto spec_len = limit == std::string::npos ?
specifier.length() :
limit + 1;
Maybe<URL> check = Resolve(env, "./node_modules/" + specifier, dir, true);
if (!check.IsNothing()) {
const size_t limit = specifier.find('/');
const size_t spec_len =
limit == std::string::npos ? specifier.length() :
limit + 1;
std::string chroot =
dir.path() + "node_modules/" + specifier.substr(0, spec_len);
if (check.path().substr(0, chroot.length()) != chroot) {
return URL("");
if (check.FromJust().path().substr(0, chroot.length()) != chroot) {
return Nothing<URL>();
}
return check;
} else {
@ -447,45 +448,51 @@ URL resolve_module(std::string specifier, const URL* base) {
}
parent = URL("..", &dir);
} 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) {
auto main = resolve_main(search);
if (!(main.flags() & URL_FLAGS_FAILED)) return main;
Maybe<URL> main = ResolveMain(env, search);
if (!main.IsNothing())
return main;
}
return resolve_index(search);
return ResolveIndex(search);
}
} // 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);
if (!(pure_url.flags() & URL_FLAGS_FAILED)) {
// just check existence, without altering
auto check = check_file(pure_url, true);
if (check.failed) {
return URL("");
Maybe<uv_file> check = CheckFile(pure_url);
if (check.IsNothing()) {
return Nothing<URL>();
}
return pure_url;
return Just(pure_url);
}
if (specifier.length() == 0) {
return URL("");
return Nothing<URL>();
}
if (is_relative_or_absolute_path(specifier)) {
if (ShouldBeTreatedAsRelativeOrAbsolutePath(specifier)) {
URL resolved(specifier, base);
auto file = resolve_extensions(resolved);
if (!(file.flags() & URL_FLAGS_FAILED)) return file;
Maybe<URL> file = ResolveExtensions<TRY_EXACT_NAME>(resolved);
if (!file.IsNothing())
return file;
if (specifier.back() != '/') {
resolved = URL(specifier + "/", base);
}
return resolve_directory(resolved, read_pkg_json);
return ResolveDirectory(env, resolved, read_pkg_json);
} else {
return resolve_module(specifier, base);
return ResolveModule(env, specifier, base);
}
UNREACHABLE();
}
void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
@ -504,34 +511,34 @@ void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
env->ThrowError("first argument is not a string");
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()) {
env->ThrowError("second argument is not a string");
return;
}
Utf8Value url_utf(env->isolate(), args[1]);
URL url(*url_utf, url_utf.length());
Utf8Value url_utf8(env->isolate(), args[1]);
URL url(*url_utf8, url_utf8.length());
if (url.flags() & URL_FLAGS_FAILED) {
env->ThrowError("second argument is not a URL string");
return;
}
URL result = node::loader::Resolve(*specifier_utf, &url, true);
if (result.flags() & URL_FLAGS_FAILED) {
std::string msg = "Cannot find module ";
msg += *specifier_utf;
Maybe<URL> result = node::loader::Resolve(env, specifier_std, url, true);
if (result.IsNothing() || (result.FromJust().flags() & URL_FLAGS_FAILED)) {
std::string msg = "Cannot find module " + specifier_std;
env->ThrowError(msg.c_str());
return;
}
args.GetReturnValue().Set(result.ToObject(env));
args.GetReturnValue().Set(result.FromJust().ToObject(env));
}
void ModuleWrap::Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context) {
Local<Value> unused,
Local<Context> context) {
Environment* env = Environment::GetCurrent(context);
Isolate* isolate = env->isolate();

View File

@ -12,8 +12,10 @@
namespace node {
namespace loader {
node::url::URL Resolve(std::string specifier, const node::url::URL* base,
bool read_pkg_json = false);
v8::Maybe<url::URL> Resolve(Environment* env,
const std::string& specifier,
const url::URL& base,
bool read_pkg_json = false);
class ModuleWrap : public BaseObject {
public:
@ -23,7 +25,7 @@ class ModuleWrap : public BaseObject {
v8::Local<v8::Context> context);
private:
ModuleWrap(node::Environment* env,
ModuleWrap(Environment* env,
v8::Local<v8::Object> object,
v8::Local<v8::Module> module,
v8::Local<v8::String> url);

View File

@ -118,6 +118,9 @@ class URL {
URL(std::string input, const URL* 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(input.c_str(), input.length(), base.c_str(), base.length()) {}
@ -168,6 +171,13 @@ class URL {
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:
struct url_data context_;
};