1 #include <algorithm>
2 #include <limits.h> // PATH_MAX
3 #include <sys/stat.h> // S_IFDIR
4 #include "module_wrap.h"
5
6 #include "env.h"
7 #include "node_errors.h"
8 #include "node_url.h"
9 #include "util-inl.h"
10 #include "node_internals.h"
11 #include "node_contextify.h"
12 #include "node_watchdog.h"
13
14 namespace node {
15 namespace loader {
16
17 using node::contextify::ContextifyContext;
18 using node::url::URL;
19 using node::url::URL_FLAGS_FAILED;
20 using v8::Array;
21 using v8::Context;
22 using v8::Function;
23 using v8::FunctionCallbackInfo;
24 using v8::FunctionTemplate;
25 using v8::HandleScope;
26 using v8::Integer;
27 using v8::IntegrityLevel;
28 using v8::Isolate;
29 using v8::JSON;
30 using v8::Just;
31 using v8::Local;
32 using v8::Maybe;
33 using v8::MaybeLocal;
34 using v8::Module;
35 using v8::Nothing;
36 using v8::Number;
37 using v8::Object;
38 using v8::PrimitiveArray;
39 using v8::Promise;
40 using v8::ScriptCompiler;
41 using v8::ScriptOrigin;
42 using v8::String;
43 using v8::TryCatch;
44 using v8::Undefined;
45 using v8::Value;
46
47 static const char* const EXTENSIONS[] = {".mjs", ".js", ".json", ".node"};
48
ModuleWrap(Environment * env,Local<Object> object,Local<Module> module,Local<String> url)49 ModuleWrap::ModuleWrap(Environment* env,
50 Local<Object> object,
51 Local<Module> module,
52 Local<String> url) :
53 BaseObject(env, object),
54 id_(env->get_next_module_id()) {
55 module_.Reset(env->isolate(), module);
56 url_.Reset(env->isolate(), url);
57 env->id_to_module_map.emplace(id_, this);
58 }
59
~ModuleWrap()60 ModuleWrap::~ModuleWrap() {
61 HandleScope scope(env()->isolate());
62 Local<Module> module = module_.Get(env()->isolate());
63 env()->id_to_module_map.erase(id_);
64 auto range = env()->hash_to_module_map.equal_range(module->GetIdentityHash());
65 for (auto it = range.first; it != range.second; ++it) {
66 if (it->second == this) {
67 env()->hash_to_module_map.erase(it);
68 break;
69 }
70 }
71 }
72
GetFromModule(Environment * env,Local<Module> module)73 ModuleWrap* ModuleWrap::GetFromModule(Environment* env,
74 Local<Module> module) {
75 auto range = env->hash_to_module_map.equal_range(module->GetIdentityHash());
76 for (auto it = range.first; it != range.second; ++it) {
77 if (it->second->module_ == module) {
78 return it->second;
79 }
80 }
81 return nullptr;
82 }
83
GetFromID(Environment * env,uint32_t id)84 ModuleWrap* ModuleWrap::GetFromID(Environment* env, uint32_t id) {
85 auto module_wrap_it = env->id_to_module_map.find(id);
86 if (module_wrap_it == env->id_to_module_map.end()) {
87 return nullptr;
88 }
89 return module_wrap_it->second;
90 }
91
New(const FunctionCallbackInfo<Value> & args)92 void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
93 Environment* env = Environment::GetCurrent(args);
94 Isolate* isolate = env->isolate();
95
96 CHECK(args.IsConstructCall());
97 Local<Object> that = args.This();
98
99 const int argc = args.Length();
100 CHECK_GE(argc, 2);
101
102 CHECK(args[0]->IsString());
103 Local<String> source_text = args[0].As<String>();
104
105 CHECK(args[1]->IsString());
106 Local<String> url = args[1].As<String>();
107
108 Local<Context> context;
109 Local<Integer> line_offset;
110 Local<Integer> column_offset;
111
112 if (argc == 5) {
113 // new ModuleWrap(source, url, context?, lineOffset, columnOffset)
114 if (args[2]->IsUndefined()) {
115 context = that->CreationContext();
116 } else {
117 CHECK(args[2]->IsObject());
118 ContextifyContext* sandbox =
119 ContextifyContext::ContextFromContextifiedSandbox(
120 env, args[2].As<Object>());
121 CHECK_NOT_NULL(sandbox);
122 context = sandbox->context();
123 }
124
125 CHECK(args[3]->IsNumber());
126 line_offset = args[3].As<Integer>();
127
128 CHECK(args[4]->IsNumber());
129 column_offset = args[4].As<Integer>();
130 } else {
131 // new ModuleWrap(source, url)
132 context = that->CreationContext();
133 line_offset = Integer::New(isolate, 0);
134 column_offset = Integer::New(isolate, 0);
135 }
136
137 Environment::ShouldNotAbortOnUncaughtScope no_abort_scope(env);
138 TryCatch try_catch(isolate);
139 Local<Module> module;
140
141 Local<PrimitiveArray> host_defined_options =
142 PrimitiveArray::New(isolate, HostDefinedOptions::kLength);
143 host_defined_options->Set(isolate, HostDefinedOptions::kType,
144 Number::New(isolate, ScriptType::kModule));
145
146 // compile
147 {
148 ScriptOrigin origin(url,
149 line_offset, // line offset
150 column_offset, // column offset
151 False(isolate), // is cross origin
152 Local<Integer>(), // script id
153 Local<Value>(), // source map URL
154 False(isolate), // is opaque (?)
155 False(isolate), // is WASM
156 True(isolate), // is ES Module
157 host_defined_options);
158 Context::Scope context_scope(context);
159 ScriptCompiler::Source source(source_text, origin);
160 if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) {
161 CHECK(try_catch.HasCaught());
162 CHECK(!try_catch.Message().IsEmpty());
163 CHECK(!try_catch.Exception().IsEmpty());
164 AppendExceptionLine(env, try_catch.Exception(), try_catch.Message(),
165 ErrorHandlingMode::MODULE_ERROR);
166 try_catch.ReThrow();
167 return;
168 }
169 }
170
171 if (!that->Set(context, env->url_string(), url).FromMaybe(false)) {
172 return;
173 }
174
175 ModuleWrap* obj = new ModuleWrap(env, that, module, url);
176 obj->context_.Reset(isolate, context);
177
178 env->hash_to_module_map.emplace(module->GetIdentityHash(), obj);
179
180 host_defined_options->Set(isolate, HostDefinedOptions::kID,
181 Number::New(isolate, obj->id()));
182
183 that->SetIntegrityLevel(context, IntegrityLevel::kFrozen);
184 args.GetReturnValue().Set(that);
185 }
186
Link(const FunctionCallbackInfo<Value> & args)187 void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
188 Environment* env = Environment::GetCurrent(args);
189 Isolate* isolate = args.GetIsolate();
190
191 CHECK_EQ(args.Length(), 1);
192 CHECK(args[0]->IsFunction());
193
194 Local<Object> that = args.This();
195
196 ModuleWrap* obj;
197 ASSIGN_OR_RETURN_UNWRAP(&obj, that);
198
199 if (obj->linked_)
200 return;
201 obj->linked_ = true;
202
203 Local<Function> resolver_arg = args[0].As<Function>();
204
205 Local<Context> mod_context = obj->context_.Get(isolate);
206 Local<Module> module = obj->module_.Get(isolate);
207
208 Local<Array> promises = Array::New(isolate,
209 module->GetModuleRequestsLength());
210
211 // call the dependency resolve callbacks
212 for (int i = 0; i < module->GetModuleRequestsLength(); i++) {
213 Local<String> specifier = module->GetModuleRequest(i);
214 Utf8Value specifier_utf8(env->isolate(), specifier);
215 std::string specifier_std(*specifier_utf8, specifier_utf8.length());
216
217 Local<Value> argv[] = {
218 specifier
219 };
220
221 MaybeLocal<Value> maybe_resolve_return_value =
222 resolver_arg->Call(mod_context, that, 1, argv);
223 if (maybe_resolve_return_value.IsEmpty()) {
224 return;
225 }
226 Local<Value> resolve_return_value =
227 maybe_resolve_return_value.ToLocalChecked();
228 if (!resolve_return_value->IsPromise()) {
229 env->ThrowError("linking error, expected resolver to return a promise");
230 }
231 Local<Promise> resolve_promise = resolve_return_value.As<Promise>();
232 obj->resolve_cache_[specifier_std].Reset(env->isolate(), resolve_promise);
233
234 promises->Set(mod_context, i, resolve_promise).FromJust();
235 }
236
237 args.GetReturnValue().Set(promises);
238 }
239
Instantiate(const FunctionCallbackInfo<Value> & args)240 void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) {
241 Environment* env = Environment::GetCurrent(args);
242 Isolate* isolate = args.GetIsolate();
243 ModuleWrap* obj;
244 ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
245 Local<Context> context = obj->context_.Get(isolate);
246 Local<Module> module = obj->module_.Get(isolate);
247 TryCatch try_catch(isolate);
248 Maybe<bool> ok = module->InstantiateModule(context, ResolveCallback);
249
250 // clear resolve cache on instantiate
251 obj->resolve_cache_.clear();
252
253 if (!ok.FromMaybe(false)) {
254 CHECK(try_catch.HasCaught());
255 CHECK(!try_catch.Message().IsEmpty());
256 CHECK(!try_catch.Exception().IsEmpty());
257 AppendExceptionLine(env, try_catch.Exception(), try_catch.Message(),
258 ErrorHandlingMode::MODULE_ERROR);
259 try_catch.ReThrow();
260 return;
261 }
262 }
263
Evaluate(const FunctionCallbackInfo<Value> & args)264 void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
265 Environment* env = Environment::GetCurrent(args);
266 Isolate* isolate = env->isolate();
267 ModuleWrap* obj;
268 ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
269 Local<Context> context = obj->context_.Get(isolate);
270 Local<Module> module = obj->module_.Get(isolate);
271
272 // module.evaluate(timeout, breakOnSigint)
273 CHECK_EQ(args.Length(), 2);
274
275 CHECK(args[0]->IsNumber());
276 int64_t timeout = args[0]->IntegerValue(env->context()).FromJust();
277
278 CHECK(args[1]->IsBoolean());
279 bool break_on_sigint = args[1]->IsTrue();
280
281 Environment::ShouldNotAbortOnUncaughtScope no_abort_scope(env);
282 TryCatch try_catch(isolate);
283
284 bool timed_out = false;
285 bool received_signal = false;
286 MaybeLocal<Value> result;
287 if (break_on_sigint && timeout != -1) {
288 Watchdog wd(isolate, timeout, &timed_out);
289 SigintWatchdog swd(isolate, &received_signal);
290 result = module->Evaluate(context);
291 } else if (break_on_sigint) {
292 SigintWatchdog swd(isolate, &received_signal);
293 result = module->Evaluate(context);
294 } else if (timeout != -1) {
295 Watchdog wd(isolate, timeout, &timed_out);
296 result = module->Evaluate(context);
297 } else {
298 result = module->Evaluate(context);
299 }
300
301 // Convert the termination exception into a regular exception.
302 if (timed_out || received_signal) {
303 env->isolate()->CancelTerminateExecution();
304 // It is possible that execution was terminated by another timeout in
305 // which this timeout is nested, so check whether one of the watchdogs
306 // from this invocation is responsible for termination.
307 if (timed_out) {
308 env->ThrowError("Script execution timed out.");
309 } else if (received_signal) {
310 env->ThrowError("Script execution interrupted.");
311 }
312 }
313
314 if (try_catch.HasCaught()) {
315 try_catch.ReThrow();
316 return;
317 }
318
319 args.GetReturnValue().Set(result.ToLocalChecked());
320 }
321
Namespace(const FunctionCallbackInfo<Value> & args)322 void ModuleWrap::Namespace(const FunctionCallbackInfo<Value>& args) {
323 Environment* env = Environment::GetCurrent(args);
324 Isolate* isolate = args.GetIsolate();
325 ModuleWrap* obj;
326 ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
327
328 Local<Module> module = obj->module_.Get(isolate);
329
330 switch (module->GetStatus()) {
331 default:
332 return env->ThrowError(
333 "cannot get namespace, Module has not been instantiated");
334 case v8::Module::Status::kInstantiated:
335 case v8::Module::Status::kEvaluating:
336 case v8::Module::Status::kEvaluated:
337 break;
338 }
339
340 Local<Value> result = module->GetModuleNamespace();
341 args.GetReturnValue().Set(result);
342 }
343
GetStatus(const FunctionCallbackInfo<Value> & args)344 void ModuleWrap::GetStatus(const FunctionCallbackInfo<Value>& args) {
345 Isolate* isolate = args.GetIsolate();
346 ModuleWrap* obj;
347 ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
348
349 Local<Module> module = obj->module_.Get(isolate);
350
351 args.GetReturnValue().Set(module->GetStatus());
352 }
353
GetStaticDependencySpecifiers(const FunctionCallbackInfo<Value> & args)354 void ModuleWrap::GetStaticDependencySpecifiers(
355 const FunctionCallbackInfo<Value>& args) {
356 Environment* env = Environment::GetCurrent(args);
357 ModuleWrap* obj;
358 ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
359
360 Local<Module> module = obj->module_.Get(env->isolate());
361
362 int count = module->GetModuleRequestsLength();
363
364 Local<Array> specifiers = Array::New(env->isolate(), count);
365
366 for (int i = 0; i < count; i++)
367 specifiers->Set(env->context(), i, module->GetModuleRequest(i)).FromJust();
368
369 args.GetReturnValue().Set(specifiers);
370 }
371
GetError(const FunctionCallbackInfo<Value> & args)372 void ModuleWrap::GetError(const FunctionCallbackInfo<Value>& args) {
373 Isolate* isolate = args.GetIsolate();
374 ModuleWrap* obj;
375 ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
376
377 Local<Module> module = obj->module_.Get(isolate);
378
379 args.GetReturnValue().Set(module->GetException());
380 }
381
ResolveCallback(Local<Context> context,Local<String> specifier,Local<Module> referrer)382 MaybeLocal<Module> ModuleWrap::ResolveCallback(Local<Context> context,
383 Local<String> specifier,
384 Local<Module> referrer) {
385 Environment* env = Environment::GetCurrent(context);
386 CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here.
387 Isolate* isolate = env->isolate();
388
389 ModuleWrap* dependent = GetFromModule(env, referrer);
390 if (dependent == nullptr) {
391 env->ThrowError("linking error, null dep");
392 return MaybeLocal<Module>();
393 }
394
395 Utf8Value specifier_utf8(isolate, specifier);
396 std::string specifier_std(*specifier_utf8, specifier_utf8.length());
397
398 if (dependent->resolve_cache_.count(specifier_std) != 1) {
399 env->ThrowError("linking error, not in local cache");
400 return MaybeLocal<Module>();
401 }
402
403 Local<Promise> resolve_promise =
404 dependent->resolve_cache_[specifier_std].Get(isolate);
405
406 if (resolve_promise->State() != Promise::kFulfilled) {
407 env->ThrowError("linking error, dependency promises must be resolved on "
408 "instantiate");
409 return MaybeLocal<Module>();
410 }
411
412 Local<Object> module_object = resolve_promise->Result().As<Object>();
413 if (module_object.IsEmpty() || !module_object->IsObject()) {
414 env->ThrowError("linking error, expected a valid module object from "
415 "resolver");
416 return MaybeLocal<Module>();
417 }
418
419 ModuleWrap* module;
420 ASSIGN_OR_RETURN_UNWRAP(&module, module_object, MaybeLocal<Module>());
421 return module->module_.Get(isolate);
422 }
423
424 namespace {
425
426 // Tests whether a path starts with /, ./ or ../
427 // In WhatWG terminology, the alternative case is called a "bare" specifier
428 // (e.g. in `import "jquery"`).
ShouldBeTreatedAsRelativeOrAbsolutePath(const std::string & specifier)429 inline bool ShouldBeTreatedAsRelativeOrAbsolutePath(
430 const std::string& specifier) {
431 size_t len = specifier.length();
432 if (len == 0)
433 return false;
434 if (specifier[0] == '/') {
435 return true;
436 } else if (specifier[0] == '.') {
437 if (len == 1 || specifier[1] == '/')
438 return true;
439 if (specifier[1] == '.') {
440 if (len == 2 || specifier[2] == '/')
441 return true;
442 }
443 }
444 return false;
445 }
446
ReadFile(uv_file file)447 std::string ReadFile(uv_file file) {
448 std::string contents;
449 uv_fs_t req;
450 char buffer_memory[4096];
451 uv_buf_t buf = uv_buf_init(buffer_memory, sizeof(buffer_memory));
452
453 do {
454 const int r = uv_fs_read(uv_default_loop(),
455 &req,
456 file,
457 &buf,
458 1,
459 contents.length(), // offset
460 nullptr);
461 uv_fs_req_cleanup(&req);
462
463 if (r <= 0)
464 break;
465 contents.append(buf.base, r);
466 } while (true);
467 return contents;
468 }
469
470 enum CheckFileOptions {
471 LEAVE_OPEN_AFTER_CHECK,
472 CLOSE_AFTER_CHECK
473 };
474
CheckFile(const std::string & path,CheckFileOptions opt=CLOSE_AFTER_CHECK)475 Maybe<uv_file> CheckFile(const std::string& path,
476 CheckFileOptions opt = CLOSE_AFTER_CHECK) {
477 uv_fs_t fs_req;
478 if (path.empty()) {
479 return Nothing<uv_file>();
480 }
481
482 uv_file fd = uv_fs_open(nullptr, &fs_req, path.c_str(), O_RDONLY, 0, nullptr);
483 uv_fs_req_cleanup(&fs_req);
484
485 if (fd < 0) {
486 return Nothing<uv_file>();
487 }
488
489 uv_fs_fstat(nullptr, &fs_req, fd, nullptr);
490 uint64_t is_directory = fs_req.statbuf.st_mode & S_IFDIR;
491 uv_fs_req_cleanup(&fs_req);
492
493 if (is_directory) {
494 CHECK_EQ(0, uv_fs_close(nullptr, &fs_req, fd, nullptr));
495 uv_fs_req_cleanup(&fs_req);
496 return Nothing<uv_file>();
497 }
498
499 if (opt == CLOSE_AFTER_CHECK) {
500 CHECK_EQ(0, uv_fs_close(nullptr, &fs_req, fd, nullptr));
501 uv_fs_req_cleanup(&fs_req);
502 }
503
504 return Just(fd);
505 }
506
507 using Exists = PackageConfig::Exists;
508 using IsValid = PackageConfig::IsValid;
509 using HasMain = PackageConfig::HasMain;
510
GetPackageConfig(Environment * env,const std::string & path)511 const PackageConfig& GetPackageConfig(Environment* env,
512 const std::string& path) {
513 auto existing = env->package_json_cache.find(path);
514 if (existing != env->package_json_cache.end()) {
515 return existing->second;
516 }
517 Maybe<uv_file> check = CheckFile(path, LEAVE_OPEN_AFTER_CHECK);
518 if (check.IsNothing()) {
519 auto entry = env->package_json_cache.emplace(path,
520 PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "" });
521 return entry.first->second;
522 }
523
524 Isolate* isolate = env->isolate();
525 v8::HandleScope handle_scope(isolate);
526
527 std::string pkg_src = ReadFile(check.FromJust());
528 uv_fs_t fs_req;
529 CHECK_EQ(0, uv_fs_close(nullptr, &fs_req, check.FromJust(), nullptr));
530 uv_fs_req_cleanup(&fs_req);
531
532 Local<String> src;
533 if (!String::NewFromUtf8(isolate,
534 pkg_src.c_str(),
535 v8::NewStringType::kNormal,
536 pkg_src.length()).ToLocal(&src)) {
537 auto entry = env->package_json_cache.emplace(path,
538 PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "" });
539 return entry.first->second;
540 }
541
542 Local<Value> pkg_json_v;
543 Local<Object> pkg_json;
544
545 if (!JSON::Parse(env->context(), src).ToLocal(&pkg_json_v) ||
546 !pkg_json_v->ToObject(env->context()).ToLocal(&pkg_json)) {
547 auto entry = env->package_json_cache.emplace(path,
548 PackageConfig { Exists::Yes, IsValid::No, HasMain::No, "" });
549 return entry.first->second;
550 }
551
552 Local<Value> pkg_main;
553 HasMain has_main = HasMain::No;
554 std::string main_std;
555 if (pkg_json->Get(env->context(), env->main_string()).ToLocal(&pkg_main)) {
556 has_main = HasMain::Yes;
557 Utf8Value main_utf8(isolate, pkg_main);
558 main_std.assign(std::string(*main_utf8, main_utf8.length()));
559 }
560
561 auto entry = env->package_json_cache.emplace(path,
562 PackageConfig { Exists::Yes, IsValid::Yes, has_main, main_std });
563 return entry.first->second;
564 }
565
566 enum ResolveExtensionsOptions {
567 TRY_EXACT_NAME,
568 ONLY_VIA_EXTENSIONS
569 };
570
571 template <ResolveExtensionsOptions options>
ResolveExtensions(const URL & search)572 Maybe<URL> ResolveExtensions(const URL& search) {
573 if (options == TRY_EXACT_NAME) {
574 std::string filePath = search.ToFilePath();
575 Maybe<uv_file> check = CheckFile(filePath);
576 if (!check.IsNothing()) {
577 return Just(search);
578 }
579 }
580
581 for (const char* extension : EXTENSIONS) {
582 URL guess(search.path() + extension, &search);
583 Maybe<uv_file> check = CheckFile(guess.ToFilePath());
584 if (!check.IsNothing()) {
585 return Just(guess);
586 }
587 }
588
589 return Nothing<URL>();
590 }
591
ResolveIndex(const URL & search)592 inline Maybe<URL> ResolveIndex(const URL& search) {
593 return ResolveExtensions<ONLY_VIA_EXTENSIONS>(URL("index", search));
594 }
595
ResolveMain(Environment * env,const URL & search)596 Maybe<URL> ResolveMain(Environment* env, const URL& search) {
597 URL pkg("package.json", &search);
598
599 const PackageConfig& pjson =
600 GetPackageConfig(env, pkg.ToFilePath());
601 // Note invalid package.json should throw in resolver
602 // currently we silently ignore which is incorrect
603 if (pjson.exists == Exists::No ||
604 pjson.is_valid == IsValid::No ||
605 pjson.has_main == HasMain::No) {
606 return Nothing<URL>();
607 }
608 if (!ShouldBeTreatedAsRelativeOrAbsolutePath(pjson.main)) {
609 return Resolve(env, "./" + pjson.main, search, IgnoreMain);
610 }
611 return Resolve(env, pjson.main, search, IgnoreMain);
612 }
613
ResolveModule(Environment * env,const std::string & specifier,const URL & base)614 Maybe<URL> ResolveModule(Environment* env,
615 const std::string& specifier,
616 const URL& base) {
617 URL parent(".", base);
618 URL dir("");
619 do {
620 dir = parent;
621 Maybe<URL> check =
622 Resolve(env, "./node_modules/" + specifier, dir, CheckMain);
623 if (!check.IsNothing()) {
624 const size_t limit = specifier.find('/');
625 const size_t spec_len =
626 limit == std::string::npos ? specifier.length() :
627 limit + 1;
628 std::string chroot =
629 dir.path() + "node_modules/" + specifier.substr(0, spec_len);
630 if (check.FromJust().path().substr(0, chroot.length()) != chroot) {
631 return Nothing<URL>();
632 }
633 return check;
634 } else {
635 // TODO(bmeck) PREVENT FALLTHROUGH
636 }
637 parent = URL("..", &dir);
638 } while (parent.path() != dir.path());
639 return Nothing<URL>();
640 }
641
ResolveDirectory(Environment * env,const URL & search,PackageMainCheck check_pjson_main)642 Maybe<URL> ResolveDirectory(Environment* env,
643 const URL& search,
644 PackageMainCheck check_pjson_main) {
645 if (check_pjson_main) {
646 Maybe<URL> main = ResolveMain(env, search);
647 if (!main.IsNothing())
648 return main;
649 }
650 return ResolveIndex(search);
651 }
652
653 } // anonymous namespace
654
Resolve(Environment * env,const std::string & specifier,const URL & base,PackageMainCheck check_pjson_main)655 Maybe<URL> Resolve(Environment* env,
656 const std::string& specifier,
657 const URL& base,
658 PackageMainCheck check_pjson_main) {
659 URL pure_url(specifier);
660 if (!(pure_url.flags() & URL_FLAGS_FAILED)) {
661 // just check existence, without altering
662 Maybe<uv_file> check = CheckFile(pure_url.ToFilePath());
663 if (check.IsNothing()) {
664 return Nothing<URL>();
665 }
666 return Just(pure_url);
667 }
668 if (specifier.length() == 0) {
669 return Nothing<URL>();
670 }
671 if (ShouldBeTreatedAsRelativeOrAbsolutePath(specifier)) {
672 URL resolved(specifier, base);
673 Maybe<URL> file = ResolveExtensions<TRY_EXACT_NAME>(resolved);
674 if (!file.IsNothing())
675 return file;
676 if (specifier.back() != '/') {
677 resolved = URL(specifier + "/", base);
678 }
679 return ResolveDirectory(env, resolved, check_pjson_main);
680 } else {
681 return ResolveModule(env, specifier, base);
682 }
683 }
684
Resolve(const FunctionCallbackInfo<Value> & args)685 void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
686 Environment* env = Environment::GetCurrent(args);
687
688 // module.resolve(specifier, url)
689 CHECK_EQ(args.Length(), 2);
690
691 CHECK(args[0]->IsString());
692 Utf8Value specifier_utf8(env->isolate(), args[0]);
693 std::string specifier_std(*specifier_utf8, specifier_utf8.length());
694
695 CHECK(args[1]->IsString());
696 Utf8Value url_utf8(env->isolate(), args[1]);
697 URL url(*url_utf8, url_utf8.length());
698
699 if (url.flags() & URL_FLAGS_FAILED) {
700 return node::THROW_ERR_INVALID_ARG_TYPE(
701 env, "second argument is not a URL string");
702 }
703
704 Maybe<URL> result = node::loader::Resolve(env, specifier_std, url);
705 if (result.IsNothing() || (result.FromJust().flags() & URL_FLAGS_FAILED)) {
706 std::string msg = "Cannot find module " + specifier_std;
707 return node::THROW_ERR_MISSING_MODULE(env, msg.c_str());
708 }
709
710 args.GetReturnValue().Set(result.FromJust().ToObject(env));
711 }
712
ImportModuleDynamically(Local<Context> context,Local<v8::ScriptOrModule> referrer,Local<String> specifier)713 static MaybeLocal<Promise> ImportModuleDynamically(
714 Local<Context> context,
715 Local<v8::ScriptOrModule> referrer,
716 Local<String> specifier) {
717 Isolate* iso = context->GetIsolate();
718 Environment* env = Environment::GetCurrent(context);
719 CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here.
720 v8::EscapableHandleScope handle_scope(iso);
721
722 Local<Function> import_callback =
723 env->host_import_module_dynamically_callback();
724
725 Local<PrimitiveArray> options = referrer->GetHostDefinedOptions();
726 if (options->Length() != HostDefinedOptions::kLength) {
727 Local<Promise::Resolver> resolver =
728 Promise::Resolver::New(context).ToLocalChecked();
729 resolver
730 ->Reject(context,
731 v8::Exception::TypeError(FIXED_ONE_BYTE_STRING(
732 context->GetIsolate(), "Invalid host defined options")))
733 .ToChecked();
734 return handle_scope.Escape(resolver->GetPromise());
735 }
736
737 Local<Value> object;
738
739 int type = options->Get(iso, HostDefinedOptions::kType)
740 .As<Number>()
741 ->Int32Value(context)
742 .ToChecked();
743 uint32_t id = options->Get(iso, HostDefinedOptions::kID)
744 .As<Number>()
745 ->Uint32Value(context)
746 .ToChecked();
747 if (type == ScriptType::kScript) {
748 contextify::ContextifyScript* wrap = env->id_to_script_map.find(id)->second;
749 object = wrap->object();
750 } else if (type == ScriptType::kModule) {
751 ModuleWrap* wrap = ModuleWrap::GetFromID(env, id);
752 object = wrap->object();
753 } else if (type == ScriptType::kFunction) {
754 object = env->id_to_function_map.find(id)->second.Get(iso);
755 } else {
756 UNREACHABLE();
757 }
758
759 Local<Value> import_args[] = {
760 object,
761 Local<Value>(specifier),
762 };
763
764 Local<Value> result;
765 if (import_callback->Call(
766 context,
767 v8::Undefined(iso),
768 arraysize(import_args),
769 import_args).ToLocal(&result)) {
770 CHECK(result->IsPromise());
771 return handle_scope.Escape(result.As<Promise>());
772 }
773
774 return MaybeLocal<Promise>();
775 }
776
SetImportModuleDynamicallyCallback(const FunctionCallbackInfo<Value> & args)777 void ModuleWrap::SetImportModuleDynamicallyCallback(
778 const FunctionCallbackInfo<Value>& args) {
779 Isolate* iso = args.GetIsolate();
780 Environment* env = Environment::GetCurrent(args);
781 HandleScope handle_scope(iso);
782
783 CHECK_EQ(args.Length(), 1);
784 CHECK(args[0]->IsFunction());
785 Local<Function> import_callback = args[0].As<Function>();
786 env->set_host_import_module_dynamically_callback(import_callback);
787
788 iso->SetHostImportModuleDynamicallyCallback(ImportModuleDynamically);
789 }
790
HostInitializeImportMetaObjectCallback(Local<Context> context,Local<Module> module,Local<Object> meta)791 void ModuleWrap::HostInitializeImportMetaObjectCallback(
792 Local<Context> context, Local<Module> module, Local<Object> meta) {
793 Environment* env = Environment::GetCurrent(context);
794 CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here.
795 ModuleWrap* module_wrap = GetFromModule(env, module);
796
797 if (module_wrap == nullptr) {
798 return;
799 }
800
801 Local<Object> wrap = module_wrap->object();
802 Local<Function> callback =
803 env->host_initialize_import_meta_object_callback();
804 Local<Value> args[] = { wrap, meta };
805 callback->Call(context, Undefined(env->isolate()), arraysize(args), args)
806 .ToLocalChecked();
807 }
808
SetInitializeImportMetaObjectCallback(const FunctionCallbackInfo<Value> & args)809 void ModuleWrap::SetInitializeImportMetaObjectCallback(
810 const FunctionCallbackInfo<Value>& args) {
811 Environment* env = Environment::GetCurrent(args);
812 Isolate* isolate = env->isolate();
813
814 CHECK_EQ(args.Length(), 1);
815 CHECK(args[0]->IsFunction());
816 Local<Function> import_meta_callback = args[0].As<Function>();
817 env->set_host_initialize_import_meta_object_callback(import_meta_callback);
818
819 isolate->SetHostInitializeImportMetaObjectCallback(
820 HostInitializeImportMetaObjectCallback);
821 }
822
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context)823 void ModuleWrap::Initialize(Local<Object> target,
824 Local<Value> unused,
825 Local<Context> context) {
826 Environment* env = Environment::GetCurrent(context);
827 Isolate* isolate = env->isolate();
828
829 Local<FunctionTemplate> tpl = env->NewFunctionTemplate(New);
830 tpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "ModuleWrap"));
831 tpl->InstanceTemplate()->SetInternalFieldCount(1);
832
833 env->SetProtoMethod(tpl, "link", Link);
834 env->SetProtoMethod(tpl, "instantiate", Instantiate);
835 env->SetProtoMethod(tpl, "evaluate", Evaluate);
836 env->SetProtoMethodNoSideEffect(tpl, "namespace", Namespace);
837 env->SetProtoMethodNoSideEffect(tpl, "getStatus", GetStatus);
838 env->SetProtoMethodNoSideEffect(tpl, "getError", GetError);
839 env->SetProtoMethodNoSideEffect(tpl, "getStaticDependencySpecifiers",
840 GetStaticDependencySpecifiers);
841
842 target->Set(FIXED_ONE_BYTE_STRING(isolate, "ModuleWrap"),
843 tpl->GetFunction(context).ToLocalChecked());
844 env->SetMethod(target, "resolve", Resolve);
845 env->SetMethod(target,
846 "setImportModuleDynamicallyCallback",
847 SetImportModuleDynamicallyCallback);
848 env->SetMethod(target,
849 "setInitializeImportMetaObjectCallback",
850 SetInitializeImportMetaObjectCallback);
851
852 #define V(name) \
853 target->Set(context, \
854 FIXED_ONE_BYTE_STRING(env->isolate(), #name), \
855 Integer::New(env->isolate(), Module::Status::name)) \
856 .FromJust()
857 V(kUninstantiated);
858 V(kInstantiating);
859 V(kInstantiated);
860 V(kEvaluating);
861 V(kEvaluated);
862 V(kErrored);
863 #undef V
864 }
865
866 } // namespace loader
867 } // namespace node
868
869 NODE_MODULE_CONTEXT_AWARE_INTERNAL(module_wrap,
870 node::loader::ModuleWrap::Initialize)
871