15 using node::url::URL_FLAGS_FAILED;
17 using v8::EscapableHandleScope;
19 using v8::FunctionCallbackInfo;
20 using v8::FunctionTemplate;
22 using v8::IntegrityLevel;
31 using v8::ScriptCompiler;
32 using v8::ScriptOrigin;
36 static const char* EXTENSIONS[] = {
".mjs",
".js",
".json",
".node"};
37 std::map<int, std::vector<ModuleWrap*>*> ModuleWrap::module_map_;
39 ModuleWrap::ModuleWrap(Environment* env,
42 Local<String> url) : BaseObject(env,
object) {
43 Isolate* iso = Isolate::GetCurrent();
44 module_.Reset(iso, module);
48 ModuleWrap::~ModuleWrap() {
49 Local<Module> module = module_.Get(Isolate::GetCurrent());
50 std::vector<ModuleWrap*>* same_hash = module_map_[module->GetIdentityHash()];
51 auto it = std::find(same_hash->begin(), same_hash->end(),
this);
53 if (it != same_hash->end()) {
61 Environment* env = Environment::GetCurrent(args);
63 Isolate* iso = args.GetIsolate();
65 if (!args.IsConstructCall()) {
66 env->ThrowError(
"constructor must be called using new");
70 if (args.Length() != 2) {
71 env->ThrowError(
"constructor must have exactly 2 arguments " 76 if (!args[0]->IsString()) {
77 env->ThrowError(
"first argument is not a string");
81 auto source_text = args[0].As<String>();
83 if (!args[1]->IsString()) {
84 env->ThrowError(
"second argument is not a string");
88 Local<String> url = args[1].As<String>();
94 ScriptOrigin origin(url,
99 FIXED_ONE_BYTE_STRING(iso,
""),
103 ScriptCompiler::Source
source(source_text, origin);
104 auto maybe_mod = ScriptCompiler::CompileModule(iso, &source);
105 if (maybe_mod.IsEmpty()) {
108 mod = maybe_mod.ToLocalChecked();
111 auto that = args.This();
112 auto ctx = that->CreationContext();
113 auto url_str = FIXED_ONE_BYTE_STRING(iso,
"url");
115 if (!that->Set(
ctx, url_str, url).FromMaybe(
false)) {
120 new ModuleWrap(Environment::GetCurrent(
ctx), that, mod, url);
122 if (ModuleWrap::module_map_.count(mod->GetIdentityHash()) == 0) {
123 ModuleWrap::module_map_[mod->GetIdentityHash()] =
124 new std::vector<ModuleWrap*>();
127 ModuleWrap::module_map_[mod->GetIdentityHash()]->push_back(obj);
130 that->SetIntegrityLevel(
ctx, IntegrityLevel::kFrozen);
131 args.GetReturnValue().Set(that);
134 void ModuleWrap::Link(
const FunctionCallbackInfo<Value>& args) {
135 Environment* env = Environment::GetCurrent(args);
136 Isolate* iso = args.GetIsolate();
137 EscapableHandleScope handle_scope(iso);
138 if (!args[0]->IsFunction()) {
139 env->ThrowError(
"first argument is not a function");
143 Local<Function> resolver_arg = args[0].As<Function>();
145 auto that = args.This();
146 ModuleWrap* obj = Unwrap<ModuleWrap>(that);
147 auto mod_context = that->CreationContext();
148 if (obj->linked_)
return;
150 Local<Module> mod(obj->module_.Get(iso));
153 for (
int i = 0; i < mod->GetModuleRequestsLength(); i++) {
154 Local<String> specifier = mod->GetModuleRequest(i);
155 Utf8Value specifier_utf(env->isolate(), specifier);
156 std::string specifier_std(*specifier_utf, specifier_utf.length());
158 Local<Value> argv[] = {
162 MaybeLocal<Value> maybe_resolve_return_value =
163 resolver_arg->Call(mod_context, that, 1, argv);
164 if (maybe_resolve_return_value.IsEmpty()) {
167 Local<Value> resolve_return_value =
168 maybe_resolve_return_value.ToLocalChecked();
169 if (!resolve_return_value->IsPromise()) {
170 env->ThrowError(
"linking error, expected resolver to return a promise");
172 Local<Promise> resolve_promise = resolve_return_value.As<Promise>();
173 obj->resolve_cache_[specifier_std] =
new Persistent<Promise>();
174 obj->resolve_cache_[specifier_std]->Reset(iso, resolve_promise);
177 args.GetReturnValue().Set(handle_scope.Escape(that));
180 void ModuleWrap::Instantiate(
const FunctionCallbackInfo<Value>& args) {
181 auto iso = args.GetIsolate();
182 auto that = args.This();
183 auto ctx = that->CreationContext();
185 ModuleWrap* obj = Unwrap<ModuleWrap>(that);
186 Local<Module> mod = obj->module_.Get(iso);
187 bool ok = mod->Instantiate(
ctx, ModuleWrap::ResolveCallback);
190 obj->resolve_cache_.clear();
197 void ModuleWrap::Evaluate(
const FunctionCallbackInfo<Value>& args) {
198 auto iso = args.GetIsolate();
199 auto that = args.This();
200 auto ctx = that->CreationContext();
201 ModuleWrap* obj = Unwrap<ModuleWrap>(that);
202 auto result = obj->module_.Get(iso)->Evaluate(
ctx);
204 if (result.IsEmpty()) {
208 auto ret = result.ToLocalChecked();
209 args.GetReturnValue().Set(ret);
212 MaybeLocal<Module> ModuleWrap::ResolveCallback(Local<Context> context,
213 Local<String> specifier,
214 Local<Module> referrer) {
215 Environment* env = Environment::GetCurrent(context);
216 Isolate* iso = Isolate::GetCurrent();
217 if (ModuleWrap::module_map_.count(referrer->GetIdentityHash()) == 0) {
218 env->ThrowError(
"linking error, unknown module");
219 return MaybeLocal<Module>();
222 std::vector<ModuleWrap*>* possible_deps =
223 ModuleWrap::module_map_[referrer->GetIdentityHash()];
224 ModuleWrap* dependent =
nullptr;
226 for (
auto possible_dep : *possible_deps) {
227 if (possible_dep->module_ == referrer) {
228 dependent = possible_dep;
232 if (dependent ==
nullptr) {
233 env->ThrowError(
"linking error, null dep");
234 return MaybeLocal<Module>();
237 Utf8Value specifier_utf(env->isolate(), specifier);
238 std::string specifier_std(*specifier_utf, specifier_utf.length());
240 if (dependent->resolve_cache_.count(specifier_std) != 1) {
241 env->ThrowError(
"linking error, not in local cache");
242 return MaybeLocal<Module>();
245 Local<Promise> resolve_promise =
246 dependent->resolve_cache_[specifier_std]->Get(iso);
248 if (resolve_promise->State() != Promise::kFulfilled) {
249 env->ThrowError(
"linking error, dependency promises must be resolved on " 251 return MaybeLocal<Module>();
254 auto module_object = resolve_promise->Result().As<Object>();
255 if (module_object.IsEmpty() || !module_object->IsObject()) {
256 env->ThrowError(
"linking error, expected a valid module object from " 258 return MaybeLocal<Module>();
262 ASSIGN_OR_RETURN_UNWRAP(&mod, module_object, MaybeLocal<Module>());
263 return mod->module_.Get(env->isolate());
269 std::string specifier =
"file://";
272 char buf[MAX_PATH * 4];
277 size_t cwd_len =
sizeof(
buf);
278 int err = uv_cwd(buf, &cwd_len);
284 return URL(specifier);
286 static URL INITIAL_CWD(__init_cwd());
287 inline bool is_relative_or_absolute_path(std::string specifier) {
288 auto len = specifier.length();
291 }
else if (specifier[0] ==
'/') {
293 }
else if (specifier[0] ==
'.') {
294 if (
len == 1 || specifier[1] ==
'/') {
296 }
else if (specifier[1] ==
'.') {
297 if (
len == 2 || specifier[2] ==
'/') {
308 inline const struct read_result read_file(uv_file
file) {
309 struct read_result ret;
312 void* base = malloc(4096);
313 if (base ==
nullptr) {
314 ret.had_error =
true;
317 uv_buf_t
buf = uv_buf_init(static_cast<char*>(base), 4096);
318 uv_fs_read(uv_default_loop(), &req,
file, &buf, 1, 0,
nullptr);
319 while (req.result > 0) {
320 src += std::string(static_cast<const char*>(buf.base), req.result);
321 uv_fs_read(uv_default_loop(), &req,
file, &buf, 1, src.length(),
nullptr);
330 inline const struct file_check check_file(URL search,
332 bool allow_dir =
false) {
333 struct file_check ret;
335 std::string path = search.ToFilePath();
339 uv_fs_open(
nullptr, &fs_req, path.c_str(), O_RDONLY, 0,
nullptr);
340 auto fd = fs_req.result;
345 uv_fs_fstat(
nullptr, &fs_req, fd,
nullptr);
346 if (fs_req.statbuf.st_mode & S_IFDIR) {
347 uv_fs_close(
nullptr, &fs_req, fd,
nullptr);
353 if (close) uv_fs_close(
nullptr, &fs_req, fd,
nullptr);
356 URL resolve_extensions(URL search,
bool check_exact =
true) {
358 auto check = check_file(search,
true);
363 for (
auto extension : EXTENSIONS) {
364 URL guess(search.path() + extension, &search);
365 auto check = check_file(guess,
true);
372 inline URL resolve_index(URL search) {
373 return resolve_extensions(URL(
"index", &search),
false);
375 URL resolve_main(URL search) {
376 URL pkg(
"package.json", &search);
377 auto check = check_file(pkg);
379 auto iso = Isolate::GetCurrent();
380 auto ctx = iso->GetCurrentContext();
381 auto read = read_file(check.file);
384 uv_fs_close(
nullptr, &fs_req, check.file,
nullptr);
385 if (read.had_error)
return URL(
"");
386 std::string pkg_src = read.source;
388 String::NewFromUtf8(iso, pkg_src.c_str(),
389 String::kNormalString, pkg_src.length());
390 if (src.IsEmpty())
return URL(
"");
391 auto maybe_pkg_json = JSON::Parse(
ctx, src);
392 if (maybe_pkg_json.IsEmpty())
return URL(
"");
393 auto pkg_json_obj = maybe_pkg_json.ToLocalChecked().As<Object>();
394 if (!pkg_json_obj->IsObject())
return URL(
"");
395 auto maybe_pkg_main = pkg_json_obj->Get(
396 ctx, FIXED_ONE_BYTE_STRING(iso,
"main"));
397 if (maybe_pkg_main.IsEmpty())
return URL(
"");
398 auto pkg_main_str = maybe_pkg_main.ToLocalChecked().As<String>();
399 if (!pkg_main_str->IsString())
return URL(
"");
400 Utf8Value main_utf8(iso, pkg_main_str);
401 std::string main_std(*main_utf8, main_utf8.length());
402 if (!is_relative_or_absolute_path(main_std)) {
403 main_std.insert(0,
"./");
405 return Resolve(main_std, &search);
409 URL resolve_module(std::string specifier, URL* base) {
410 URL parent(
".", base);
414 auto check =
Resolve(
"./node_modules/" + specifier, &dir,
true);
415 if (!(check.flags() & URL_FLAGS_FAILED)) {
416 const auto limit = specifier.find(
'/');
417 const auto spec_len = limit == std::string::npos ?
421 dir.path() +
"node_modules/" + specifier.substr(0, spec_len);
422 if (check.path().substr(0, chroot.length()) != chroot) {
429 parent = URL(
"..", &dir);
430 }
while (parent.path() != dir.path());
434 URL resolve_directory(URL search,
bool read_pkg_json) {
436 auto main = resolve_main(search);
437 if (!(
main.flags() & URL_FLAGS_FAILED))
return main;
439 return resolve_index(search);
445 URL
Resolve(std::string specifier, URL* base,
bool read_pkg_json) {
446 URL pure_url(specifier);
447 if (!(pure_url.flags() & URL_FLAGS_FAILED)) {
450 if (specifier.length() == 0) {
453 if (is_relative_or_absolute_path(specifier)) {
454 URL resolved(specifier, base);
455 auto file = resolve_extensions(resolved);
456 if (!(
file.flags() & URL_FLAGS_FAILED))
return file;
457 if (specifier.back() !=
'/') {
458 resolved = URL(specifier +
"/", base);
460 return resolve_directory(resolved, read_pkg_json);
462 return resolve_module(specifier, base);
468 Environment* env = Environment::GetCurrent(args);
470 if (args.IsConstructCall()) {
471 env->ThrowError(
"resolve() must not be called as a constructor");
474 if (args.Length() != 2) {
475 env->ThrowError(
"resolve must have exactly 2 arguments (string, string)");
479 if (!args[0]->IsString()) {
480 env->ThrowError(
"first argument is not a string");
483 Utf8Value specifier_utf(env->isolate(), args[0]);
485 if (!args[1]->IsString()) {
486 env->ThrowError(
"second argument is not a string");
489 Utf8Value url_utf(env->isolate(), args[1]);
490 URL url(*url_utf, url_utf.length());
492 if (url.flags() & URL_FLAGS_FAILED) {
493 env->ThrowError(
"second argument is not a URL string");
498 if (result.flags() & URL_FLAGS_FAILED) {
499 std::string msg =
"module ";
500 msg += *specifier_utf;
502 env->ThrowError(msg.c_str());
506 args.GetReturnValue().Set(result.ToObject(env));
511 Local<Context> context) {
512 Environment* env = Environment::GetCurrent(context);
513 Isolate* isolate = env->isolate();
515 Local<FunctionTemplate> tpl = env->NewFunctionTemplate(
New);
516 tpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate,
"ModuleWrap"));
517 tpl->InstanceTemplate()->SetInternalFieldCount(1);
519 env->SetProtoMethod(tpl,
"link", Link);
520 env->SetProtoMethod(tpl,
"instantiate", Instantiate);
521 env->SetProtoMethod(tpl,
"evaluate", Evaluate);
523 target->Set(FIXED_ONE_BYTE_STRING(isolate,
"ModuleWrap"), tpl->GetFunction());
NODE_MODULE_CONTEXT_AWARE_BUILTIN(inspector, node::inspector::Agent::InitInspector)
int main(int argc, char *argv[])
void Initialize(Local< Object > target, Local< Value > unused, Local< Context > context, void *priv)
MaybeLocal< Object > New(Isolate *isolate, Local< String > string, enum encoding enc)
URL Resolve(std::string specifier, URL *base, bool read_pkg_json)