GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/module_wrap.cc Lines: 488 576 84.7 %
Date: 2019-05-05 22:32:45 Branches: 237 390 60.8 %

Line Branch Exec Source
1
#include "module_wrap.h"
2
3
#include "env.h"
4
#include "node_errors.h"
5
#include "node_url.h"
6
#include "util-inl.h"
7
#include "node_contextify.h"
8
#include "node_watchdog.h"
9
10
#include <sys/stat.h>  // S_IFDIR
11
12
#include <algorithm>
13
#include <climits>  // PATH_MAX
14
15
namespace node {
16
namespace loader {
17
18
using errors::TryCatchScope;
19
20
using node::contextify::ContextifyContext;
21
using node::url::URL;
22
using node::url::URL_FLAGS_FAILED;
23
using v8::Array;
24
using v8::Context;
25
using v8::Function;
26
using v8::FunctionCallbackInfo;
27
using v8::FunctionTemplate;
28
using v8::Global;
29
using v8::HandleScope;
30
using v8::Integer;
31
using v8::IntegrityLevel;
32
using v8::Isolate;
33
using v8::Just;
34
using v8::Local;
35
using v8::Maybe;
36
using v8::MaybeLocal;
37
using v8::Module;
38
using v8::Nothing;
39
using v8::Number;
40
using v8::Object;
41
using v8::PrimitiveArray;
42
using v8::Promise;
43
using v8::ScriptCompiler;
44
using v8::ScriptOrigin;
45
using v8::String;
46
using v8::Undefined;
47
using v8::Value;
48
49
static const char* const EXTENSIONS[] = {
50
  ".mjs",
51
  ".cjs",
52
  ".js",
53
  ".json",
54
  ".node"
55
};
56
57
379
ModuleWrap::ModuleWrap(Environment* env,
58
                       Local<Object> object,
59
                       Local<Module> module,
60
                       Local<String> url) :
61
  BaseObject(env, object),
62
1516
  id_(env->get_next_module_id()) {
63
379
  module_.Reset(env->isolate(), module);
64
379
  url_.Reset(env->isolate(), url);
65
379
  env->id_to_module_map.emplace(id_, this);
66
379
}
67
68
2160
ModuleWrap::~ModuleWrap() {
69
360
  HandleScope scope(env()->isolate());
70
720
  Local<Module> module = module_.Get(env()->isolate());
71
360
  env()->id_to_module_map.erase(id_);
72
360
  auto range = env()->hash_to_module_map.equal_range(module->GetIdentityHash());
73
360
  for (auto it = range.first; it != range.second; ++it) {
74
360
    if (it->second == this) {
75
360
      env()->hash_to_module_map.erase(it);
76
360
      break;
77
    }
78
360
  }
79
720
}
80
81
351
ModuleWrap* ModuleWrap::GetFromModule(Environment* env,
82
                                      Local<Module> module) {
83
351
  auto range = env->hash_to_module_map.equal_range(module->GetIdentityHash());
84
351
  for (auto it = range.first; it != range.second; ++it) {
85
702
    if (it->second->module_ == module) {
86
351
      return it->second;
87
    }
88
  }
89
  return nullptr;
90
}
91
92
13
ModuleWrap* ModuleWrap::GetFromID(Environment* env, uint32_t id) {
93
13
  auto module_wrap_it = env->id_to_module_map.find(id);
94
13
  if (module_wrap_it == env->id_to_module_map.end()) {
95
    return nullptr;
96
  }
97
13
  return module_wrap_it->second;
98
}
99
100
384
void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
101
384
  Environment* env = Environment::GetCurrent(args);
102
384
  Isolate* isolate = env->isolate();
103
104
384
  CHECK(args.IsConstructCall());
105
384
  Local<Object> that = args.This();
106
107
384
  const int argc = args.Length();
108
384
  CHECK_GE(argc, 2);
109
110
1152
  CHECK(args[0]->IsString());
111
768
  Local<String> source_text = args[0].As<String>();
112
113
1152
  CHECK(args[1]->IsString());
114
768
  Local<String> url = args[1].As<String>();
115
116
  Local<Context> context;
117
  Local<Integer> line_offset;
118
  Local<Integer> column_offset;
119
120
384
  if (argc == 5) {
121
    // new ModuleWrap(source, url, context?, lineOffset, columnOffset)
122
165
    if (args[2]->IsUndefined()) {
123
49
      context = that->CreationContext();
124
    } else {
125
12
      CHECK(args[2]->IsObject());
126
      ContextifyContext* sandbox =
127
          ContextifyContext::ContextFromContextifiedSandbox(
128
12
              env, args[2].As<Object>());
129
6
      CHECK_NOT_NULL(sandbox);
130
6
      context = sandbox->context();
131
    }
132
133
110
    CHECK(args[3]->IsNumber());
134
110
    line_offset = args[3].As<Integer>();
135
136
110
    CHECK(args[4]->IsNumber());
137
110
    column_offset = args[4].As<Integer>();
138
  } else {
139
    // new ModuleWrap(source, url)
140
329
    context = that->CreationContext();
141
329
    line_offset = Integer::New(isolate, 0);
142
329
    column_offset = Integer::New(isolate, 0);
143
  }
144
145
384
  ShouldNotAbortOnUncaughtScope no_abort_scope(env);
146
763
  TryCatchScope try_catch(env);
147
  Local<Module> module;
148
149
  Local<PrimitiveArray> host_defined_options =
150
384
      PrimitiveArray::New(isolate, HostDefinedOptions::kLength);
151
  host_defined_options->Set(isolate, HostDefinedOptions::kType,
152
768
                            Number::New(isolate, ScriptType::kModule));
153
154
  // compile
155
  {
156
    ScriptOrigin origin(url,
157
                        line_offset,                          // line offset
158
                        column_offset,                        // column offset
159
                        True(isolate),                        // is cross origin
160
                        Local<Integer>(),                     // script id
161
                        Local<Value>(),                       // source map URL
162
                        False(isolate),                       // is opaque (?)
163
                        False(isolate),                       // is WASM
164
                        True(isolate),                        // is ES Module
165
384
                        host_defined_options);
166
    Context::Scope context_scope(context);
167
379
    ScriptCompiler::Source source(source_text, origin);
168
768
    if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) {
169

5
      if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
170
10
        CHECK(!try_catch.Message().IsEmpty());
171
10
        CHECK(!try_catch.Exception().IsEmpty());
172
        AppendExceptionLine(env, try_catch.Exception(), try_catch.Message(),
173
5
                            ErrorHandlingMode::MODULE_ERROR);
174
5
        try_catch.ReThrow();
175
      }
176
5
      return;
177
379
    }
178
  }
179
180
1516
  if (!that->Set(context, env->url_string(), url).FromMaybe(false)) {
181
    return;
182
  }
183
184
379
  ModuleWrap* obj = new ModuleWrap(env, that, module, url);
185
379
  obj->context_.Reset(isolate, context);
186
187
379
  env->hash_to_module_map.emplace(module->GetIdentityHash(), obj);
188
189
  host_defined_options->Set(isolate, HostDefinedOptions::kID,
190
758
                            Number::New(isolate, obj->id()));
191
192
379
  that->SetIntegrityLevel(context, IntegrityLevel::kFrozen);
193
1137
  args.GetReturnValue().Set(that);
194
}
195
196
561
void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
197
561
  Environment* env = Environment::GetCurrent(args);
198
561
  Isolate* isolate = args.GetIsolate();
199
200
561
  CHECK_EQ(args.Length(), 1);
201
1122
  CHECK(args[0]->IsFunction());
202
203
561
  Local<Object> that = args.This();
204
205
  ModuleWrap* obj;
206
762
  ASSIGN_OR_RETURN_UNWRAP(&obj, that);
207
208
561
  if (obj->linked_)
209
201
    return;
210
360
  obj->linked_ = true;
211
212
720
  Local<Function> resolver_arg = args[0].As<Function>();
213
214
720
  Local<Context> mod_context = obj->context_.Get(isolate);
215
720
  Local<Module> module = obj->module_.Get(isolate);
216
217
  Local<Array> promises = Array::New(isolate,
218
360
                                     module->GetModuleRequestsLength());
219
220
  // call the dependency resolve callbacks
221
1120
  for (int i = 0; i < module->GetModuleRequestsLength(); i++) {
222
200
    Local<String> specifier = module->GetModuleRequest(i);
223
200
    Utf8Value specifier_utf8(env->isolate(), specifier);
224
400
    std::string specifier_std(*specifier_utf8, specifier_utf8.length());
225
226
    Local<Value> argv[] = {
227
      specifier
228
400
    };
229
230
    MaybeLocal<Value> maybe_resolve_return_value =
231
200
        resolver_arg->Call(mod_context, that, 1, argv);
232
200
    if (maybe_resolve_return_value.IsEmpty()) {
233
      return;
234
    }
235
    Local<Value> resolve_return_value =
236
200
        maybe_resolve_return_value.ToLocalChecked();
237
200
    if (!resolve_return_value->IsPromise()) {
238
      env->ThrowError("linking error, expected resolver to return a promise");
239
    }
240
200
    Local<Promise> resolve_promise = resolve_return_value.As<Promise>();
241
200
    obj->resolve_cache_[specifier_std].Reset(env->isolate(), resolve_promise);
242
243
600
    promises->Set(mod_context, i, resolve_promise).Check();
244
200
  }
245
246
720
  args.GetReturnValue().Set(promises);
247
}
248
249
303
void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) {
250
303
  Environment* env = Environment::GetCurrent(args);
251
303
  Isolate* isolate = args.GetIsolate();
252
  ModuleWrap* obj;
253
306
  ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
254
606
  Local<Context> context = obj->context_.Get(isolate);
255
606
  Local<Module> module = obj->module_.Get(isolate);
256
303
  TryCatchScope try_catch(env);
257
303
  USE(module->InstantiateModule(context, ResolveCallback));
258
259
  // clear resolve cache on instantiate
260
303
  obj->resolve_cache_.clear();
261
262

303
  if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
263
6
    CHECK(!try_catch.Message().IsEmpty());
264
6
    CHECK(!try_catch.Exception().IsEmpty());
265
    AppendExceptionLine(env, try_catch.Exception(), try_catch.Message(),
266
3
                        ErrorHandlingMode::MODULE_ERROR);
267
3
    try_catch.ReThrow();
268
3
    return;
269
300
  }
270
}
271
272
106
void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
273
106
  Environment* env = Environment::GetCurrent(args);
274
106
  Isolate* isolate = env->isolate();
275
  ModuleWrap* obj;
276
113
  ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
277
212
  Local<Context> context = obj->context_.Get(isolate);
278
212
  Local<Module> module = obj->module_.Get(isolate);
279
280
  // module.evaluate(timeout, breakOnSigint)
281
106
  CHECK_EQ(args.Length(), 2);
282
283
212
  CHECK(args[0]->IsNumber());
284
424
  int64_t timeout = args[0]->IntegerValue(env->context()).FromJust();
285
286
212
  CHECK(args[1]->IsBoolean());
287
212
  bool break_on_sigint = args[1]->IsTrue();
288
289
106
  ShouldNotAbortOnUncaughtScope no_abort_scope(env);
290
204
  TryCatchScope try_catch(env);
291
292
106
  bool timed_out = false;
293
106
  bool received_signal = false;
294
  MaybeLocal<Value> result;
295

106
  if (break_on_sigint && timeout != -1) {
296
    Watchdog wd(isolate, timeout, &timed_out);
297
    SigintWatchdog swd(isolate, &received_signal);
298
    result = module->Evaluate(context);
299
106
  } else if (break_on_sigint) {
300
    SigintWatchdog swd(isolate, &received_signal);
301
    result = module->Evaluate(context);
302
106
  } else if (timeout != -1) {
303
1
    Watchdog wd(isolate, timeout, &timed_out);
304
1
    result = module->Evaluate(context);
305
  } else {
306
105
    result = module->Evaluate(context);
307
  }
308
309
  // Convert the termination exception into a regular exception.
310

105
  if (timed_out || received_signal) {
311

1
    if (!env->is_main_thread() && env->is_stopping())
312
      return;
313
1
    env->isolate()->CancelTerminateExecution();
314
    // It is possible that execution was terminated by another timeout in
315
    // which this timeout is nested, so check whether one of the watchdogs
316
    // from this invocation is responsible for termination.
317
1
    if (timed_out) {
318
1
      THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(env, timeout);
319
    } else if (received_signal) {
320
      THROW_ERR_SCRIPT_EXECUTION_INTERRUPTED(env);
321
    }
322
  }
323
324
105
  if (try_catch.HasCaught()) {
325
7
    if (!try_catch.HasTerminated())
326
6
      try_catch.ReThrow();
327
7
    return;
328
  }
329
330
294
  args.GetReturnValue().Set(result.ToLocalChecked());
331
}
332
333
291
void ModuleWrap::Namespace(const FunctionCallbackInfo<Value>& args) {
334
291
  Environment* env = Environment::GetCurrent(args);
335
291
  Isolate* isolate = args.GetIsolate();
336
  ModuleWrap* obj;
337
291
  ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
338
339
582
  Local<Module> module = obj->module_.Get(isolate);
340
341
291
  switch (module->GetStatus()) {
342
    default:
343
      return env->ThrowError(
344
          "cannot get namespace, Module has not been instantiated");
345
    case v8::Module::Status::kInstantiated:
346
    case v8::Module::Status::kEvaluating:
347
    case v8::Module::Status::kEvaluated:
348
291
      break;
349
  }
350
351
291
  Local<Value> result = module->GetModuleNamespace();
352
582
  args.GetReturnValue().Set(result);
353
}
354
355
104
void ModuleWrap::GetStatus(const FunctionCallbackInfo<Value>& args) {
356
104
  Isolate* isolate = args.GetIsolate();
357
  ModuleWrap* obj;
358
208
  ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
359
360
208
  Local<Module> module = obj->module_.Get(isolate);
361
362
312
  args.GetReturnValue().Set(module->GetStatus());
363
}
364
365
1
void ModuleWrap::GetStaticDependencySpecifiers(
366
    const FunctionCallbackInfo<Value>& args) {
367
1
  Environment* env = Environment::GetCurrent(args);
368
  ModuleWrap* obj;
369
2
  ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
370
371
2
  Local<Module> module = obj->module_.Get(env->isolate());
372
373
1
  int count = module->GetModuleRequestsLength();
374
375
1
  Local<Array> specifiers = Array::New(env->isolate(), count);
376
377
2
  for (int i = 0; i < count; i++)
378
4
    specifiers->Set(env->context(), i, module->GetModuleRequest(i)).Check();
379
380
2
  args.GetReturnValue().Set(specifiers);
381
}
382
383
1
void ModuleWrap::GetError(const FunctionCallbackInfo<Value>& args) {
384
1
  Isolate* isolate = args.GetIsolate();
385
  ModuleWrap* obj;
386
2
  ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
387
388
2
  Local<Module> module = obj->module_.Get(isolate);
389
3
  args.GetReturnValue().Set(module->GetException());
390
}
391
392
190
MaybeLocal<Module> ModuleWrap::ResolveCallback(Local<Context> context,
393
                                               Local<String> specifier,
394
                                               Local<Module> referrer) {
395
190
  Environment* env = Environment::GetCurrent(context);
396
190
  CHECK_NOT_NULL(env);  // TODO(addaleax): Handle nullptr here.
397
190
  Isolate* isolate = env->isolate();
398
399
190
  ModuleWrap* dependent = GetFromModule(env, referrer);
400
190
  if (dependent == nullptr) {
401
    env->ThrowError("linking error, null dep");
402
    return MaybeLocal<Module>();
403
  }
404
405
190
  Utf8Value specifier_utf8(isolate, specifier);
406
380
  std::string specifier_std(*specifier_utf8, specifier_utf8.length());
407
408
190
  if (dependent->resolve_cache_.count(specifier_std) != 1) {
409
    env->ThrowError("linking error, not in local cache");
410
    return MaybeLocal<Module>();
411
  }
412
413
  Local<Promise> resolve_promise =
414
380
      dependent->resolve_cache_[specifier_std].Get(isolate);
415
416
190
  if (resolve_promise->State() != Promise::kFulfilled) {
417
    env->ThrowError("linking error, dependency promises must be resolved on "
418
                    "instantiate");
419
    return MaybeLocal<Module>();
420
  }
421
422
380
  Local<Object> module_object = resolve_promise->Result().As<Object>();
423

380
  if (module_object.IsEmpty() || !module_object->IsObject()) {
424
    env->ThrowError("linking error, expected a valid module object from "
425
                    "resolver");
426
    return MaybeLocal<Module>();
427
  }
428
429
  ModuleWrap* module;
430
190
  ASSIGN_OR_RETURN_UNWRAP(&module, module_object, MaybeLocal<Module>());
431
570
  return module->module_.Get(isolate);
432
}
433
434
namespace {
435
436
// Tests whether a path starts with /, ./ or ../
437
// In WhatWG terminology, the alternative case is called a "bare" specifier
438
// (e.g. in `import "jquery"`).
439
172
inline bool ShouldBeTreatedAsRelativeOrAbsolutePath(
440
    const std::string& specifier) {
441
172
  size_t len = specifier.length();
442
172
  if (len == 0)
443
    return false;
444
172
  if (specifier[0] == '/') {
445
72
    return true;
446
100
  } else if (specifier[0] == '.') {
447

91
    if (len == 1 || specifier[1] == '/')
448
29
      return true;
449
62
    if (specifier[1] == '.') {
450

62
      if (len == 2 || specifier[2] == '/')
451
62
        return true;
452
    }
453
  }
454
9
  return false;
455
}
456
457
16
std::string ReadFile(uv_file file) {
458
16
  std::string contents;
459
  uv_fs_t req;
460
  char buffer_memory[4096];
461
16
  uv_buf_t buf = uv_buf_init(buffer_memory, sizeof(buffer_memory));
462
463
  do {
464
    const int r = uv_fs_read(uv_default_loop(),
465
                             &req,
466
                             file,
467
                             &buf,
468
                             1,
469
32
                             contents.length(),  // offset
470
32
                             nullptr);
471
32
    uv_fs_req_cleanup(&req);
472
473
32
    if (r <= 0)
474
16
      break;
475
16
    contents.append(buf.base, r);
476
  } while (true);
477
16
  return contents;
478
}
479
480
enum DescriptorType {
481
  FILE,
482
  DIRECTORY,
483
  NONE
484
};
485
486
// When DescriptorType cache is added, this can also return
487
// Nothing for the "null" cache entries.
488
891
inline Maybe<uv_file> OpenDescriptor(const std::string& path) {
489
  uv_fs_t fs_req;
490
891
  uv_file fd = uv_fs_open(nullptr, &fs_req, path.c_str(), O_RDONLY, 0, nullptr);
491
891
  uv_fs_req_cleanup(&fs_req);
492
891
  if (fd < 0) return Nothing<uv_file>();
493
181
  return Just(fd);
494
}
495
496
181
inline void CloseDescriptor(uv_file fd) {
497
  uv_fs_t fs_req;
498
181
  CHECK_EQ(0, uv_fs_close(nullptr, &fs_req, fd, nullptr));
499
181
  uv_fs_req_cleanup(&fs_req);
500
181
}
501
502
181
inline DescriptorType CheckDescriptorAtFile(uv_file fd) {
503
  uv_fs_t fs_req;
504
181
  int rc = uv_fs_fstat(nullptr, &fs_req, fd, nullptr);
505
181
  if (rc == 0) {
506
181
    uint64_t is_directory = fs_req.statbuf.st_mode & S_IFDIR;
507
181
    uv_fs_req_cleanup(&fs_req);
508
181
    return is_directory ? DIRECTORY : FILE;
509
  }
510
  uv_fs_req_cleanup(&fs_req);
511
  return NONE;
512
}
513
514
// TODO(@guybedford): Add a DescriptorType cache layer here.
515
// Should be directory based -> if path/to/dir doesn't exist
516
// then the cache should early-fail any path/to/dir/file check.
517
235
DescriptorType CheckDescriptorAtPath(const std::string& path) {
518
235
  Maybe<uv_file> fd = OpenDescriptor(path);
519
235
  if (fd.IsNothing()) return NONE;
520
165
  DescriptorType type = CheckDescriptorAtFile(fd.FromJust());
521
165
  CloseDescriptor(fd.FromJust());
522
165
  return type;
523
}
524
525
656
Maybe<std::string> ReadIfFile(const std::string& path) {
526
656
  Maybe<uv_file> fd = OpenDescriptor(path);
527
656
  if (fd.IsNothing()) return Nothing<std::string>();
528
16
  DescriptorType type = CheckDescriptorAtFile(fd.FromJust());
529
16
  if (type != FILE) return Nothing<std::string>();
530
16
  std::string source = ReadFile(fd.FromJust());
531
16
  CloseDescriptor(fd.FromJust());
532
16
  return Just(source);
533
}
534
535
using Exists = PackageConfig::Exists;
536
using IsValid = PackageConfig::IsValid;
537
using HasMain = PackageConfig::HasMain;
538
using PackageType = PackageConfig::PackageType;
539
540
1337
Maybe<const PackageConfig*> GetPackageConfig(Environment* env,
541
                                             const std::string& path,
542
                                             const URL& base) {
543
1337
  auto existing = env->package_json_cache.find(path);
544
1337
  if (existing != env->package_json_cache.end()) {
545
681
    const PackageConfig* pcfg = &existing->second;
546
681
    if (pcfg->is_valid == IsValid::No) {
547
      std::string msg = "Invalid JSON in '" + path +
548
        "' imported from " + base.ToFilePath();
549
      node::THROW_ERR_INVALID_PACKAGE_CONFIG(env, msg.c_str());
550
      return Nothing<const PackageConfig*>();
551
    }
552
681
    return Just(pcfg);
553
  }
554
555
656
  Maybe<std::string> source = ReadIfFile(path);
556
557
656
  if (source.IsNothing()) {
558
    auto entry = env->package_json_cache.emplace(path,
559
        PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "",
560
640
                        PackageType::None });
561
640
    return Just(&entry.first->second);
562
  }
563
564
16
  std::string pkg_src = source.FromJust();
565
566
16
  Isolate* isolate = env->isolate();
567
32
  v8::HandleScope handle_scope(isolate);
568
569
  Local<Object> pkg_json;
570
  {
571
    Local<Value> src;
572
    Local<Value> pkg_json_v;
573
16
    Local<Context> context = env->context();
574
575

96
    if (!ToV8Value(context, pkg_src).ToLocal(&src) ||
576

128
        !v8::JSON::Parse(context, src.As<String>()).ToLocal(&pkg_json_v) ||
577
48
        !pkg_json_v->ToObject(context).ToLocal(&pkg_json)) {
578
      env->package_json_cache.emplace(path,
579
          PackageConfig { Exists::Yes, IsValid::No, HasMain::No, "",
580
                          PackageType::None });
581
      std::string msg = "Invalid JSON in '" + path +
582
          "' imported from " + base.ToFilePath();
583
      node::THROW_ERR_INVALID_PACKAGE_CONFIG(env, msg.c_str());
584
      return Nothing<const PackageConfig*>();
585
    }
586
  }
587
588
  Local<Value> pkg_main;
589
16
  HasMain has_main = HasMain::No;
590
32
  std::string main_std;
591
64
  if (pkg_json->Get(env->context(), env->main_string()).ToLocal(&pkg_main)) {
592
32
    if (pkg_main->IsString()) {
593
13
      has_main = HasMain::Yes;
594
    }
595
16
    Utf8Value main_utf8(isolate, pkg_main);
596
16
    main_std.assign(std::string(*main_utf8, main_utf8.length()));
597
  }
598
599
16
  PackageType pkg_type = PackageType::None;
600
  Local<Value> type_v;
601
64
  if (pkg_json->Get(env->context(), env->type_string()).ToLocal(&type_v)) {
602
32
    if (type_v->StrictEquals(env->module_string())) {
603
6
      pkg_type = PackageType::Module;
604
20
    } else if (type_v->StrictEquals(env->commonjs_string())) {
605
4
      pkg_type = PackageType::CommonJS;
606
    }
607
    // ignore unknown types for forwards compatibility
608
  }
609
610
  Local<Value> exports_v;
611
32
  if (pkg_json->Get(env->context(),
612


112
      env->exports_string()).ToLocal(&exports_v) &&
613

64
      (exports_v->IsObject() || exports_v->IsString() ||
614
16
      exports_v->IsBoolean())) {
615
    Global<Value> exports;
616
    exports.Reset(env->isolate(), exports_v);
617
618
    auto entry = env->package_json_cache.emplace(path,
619
        PackageConfig { Exists::Yes, IsValid::Yes, has_main, main_std,
620
                        pkg_type });
621
    return Just(&entry.first->second);
622
  }
623
624
  auto entry = env->package_json_cache.emplace(path,
625
      PackageConfig { Exists::Yes, IsValid::Yes, has_main, main_std,
626
16
                      pkg_type });
627
672
  return Just(&entry.first->second);
628
}
629
630
156
Maybe<const PackageConfig*> GetPackageScopeConfig(Environment* env,
631
                                                  const URL& resolved,
632
                                                  const URL& base) {
633
156
  URL pjson_url("./package.json", &resolved);
634
  while (true) {
635
    Maybe<const PackageConfig*> pkg_cfg =
636
1333
        GetPackageConfig(env, pjson_url.ToFilePath(), base);
637
1489
    if (pkg_cfg.IsNothing()) return pkg_cfg;
638
1333
    if (pkg_cfg.FromJust()->exists == Exists::Yes) return pkg_cfg;
639
640
1309
    URL last_pjson_url = pjson_url;
641
1309
    pjson_url = URL("../package.json", pjson_url);
642
643
    // Terminates at root where ../package.json equals ../../package.json
644
    // (can't just check "/package.json" for Windows support).
645
1309
    if (pjson_url.path() == last_pjson_url.path()) {
646
      auto entry = env->package_json_cache.emplace(pjson_url.ToFilePath(),
647
          PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "",
648
132
                          PackageType::None });
649
132
      const PackageConfig* pcfg = &entry.first->second;
650
132
      return Just(pcfg);
651
    }
652
1333
  }
653
}
654
655
/*
656
 * Legacy CommonJS main resolution:
657
 * 1. let M = pkg_url + (json main field)
658
 * 2. TRY(M, M.js, M.json, M.node)
659
 * 3. TRY(M/index.js, M/index.json, M/index.node)
660
 * 4. TRY(pkg_url/index.js, pkg_url/index.json, pkg_url/index.node)
661
 * 5. NOT_FOUND
662
 */
663
56
inline bool FileExists(const URL& url) {
664
56
  return CheckDescriptorAtPath(url.ToFilePath()) == FILE;
665
}
666
Maybe<URL> LegacyMainResolve(const URL& pjson_url,
667
                             const PackageConfig& pcfg) {
668
  URL guess;
669
  if (pcfg.has_main == HasMain::Yes) {
670
    // Note: fs check redundances will be handled by Descriptor cache here.
671
    if (FileExists(guess = URL("./" + pcfg.main, pjson_url))) {
672
      return Just(guess);
673
    }
674
    if (FileExists(guess = URL("./" + pcfg.main + ".js", pjson_url))) {
675
      return Just(guess);
676
    }
677
    if (FileExists(guess = URL("./" + pcfg.main + ".json", pjson_url))) {
678
      return Just(guess);
679
    }
680
    if (FileExists(guess = URL("./" + pcfg.main + ".node", pjson_url))) {
681
      return Just(guess);
682
    }
683
    if (FileExists(guess = URL("./" + pcfg.main + "/index.js", pjson_url))) {
684
      return Just(guess);
685
    }
686
    // Such stat.
687
    if (FileExists(guess = URL("./" + pcfg.main + "/index.json", pjson_url))) {
688
      return Just(guess);
689
    }
690
    if (FileExists(guess = URL("./" + pcfg.main + "/index.node", pjson_url))) {
691
      return Just(guess);
692
    }
693
    // Fallthrough.
694
  }
695
  if (FileExists(guess = URL("./index.js", pjson_url))) {
696
    return Just(guess);
697
  }
698
  // So fs.
699
  if (FileExists(guess = URL("./index.json", pjson_url))) {
700
    return Just(guess);
701
  }
702
  if (FileExists(guess = URL("./index.node", pjson_url))) {
703
    return Just(guess);
704
  }
705
  // Not found.
706
  return Nothing<URL>();
707
}
708
709
enum ResolveExtensionsOptions {
710
  TRY_EXACT_NAME,
711
  ONLY_VIA_EXTENSIONS
712
};
713
714
template <ResolveExtensionsOptions options>
715
22
Maybe<URL> ResolveExtensions(const URL& search) {
716
  if (options == TRY_EXACT_NAME) {
717
17
    if (FileExists(search)) {
718
9
      return Just(search);
719
    }
720
  }
721
722

45
  for (const char* extension : EXTENSIONS) {
723
39
    URL guess(search.path() + extension, &search);
724

39
    if (FileExists(guess)) {
725

7
      return Just(guess);
726
    }
727
  }
728
729
6
  return Nothing<URL>();
730
}
731
732
5
inline Maybe<URL> ResolveIndex(const URL& search) {
733
5
  return ResolveExtensions<ONLY_VIA_EXTENSIONS>(URL("index", search));
734
}
735
736
169
Maybe<URL> FinalizeResolution(Environment* env,
737
                              const URL& resolved,
738
                              const URL& base) {
739
169
  if (env->options()->es_module_specifier_resolution == "node") {
740
17
    Maybe<URL> file = ResolveExtensions<TRY_EXACT_NAME>(resolved);
741
17
    if (!file.IsNothing()) {
742
12
      return file;
743
    }
744
5
    if (resolved.path().back() != '/') {
745
4
      file = ResolveIndex(URL(resolved.path() + "/", &base));
746
    } else {
747
1
      file = ResolveIndex(resolved);
748
    }
749
5
    if (!file.IsNothing()) {
750
4
      return file;
751
    }
752
2
    std::string msg = "Cannot find module '" + resolved.path() +
753
4
        "' imported from " + base.ToFilePath();
754
1
    node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str());
755
18
    return Nothing<URL>();
756
  }
757
758
152
  const std::string& path = resolved.ToFilePath();
759
152
  if (CheckDescriptorAtPath(path) != FILE) {
760
26
    std::string msg = "Cannot find module '" + path +
761
39
        "' imported from " + base.ToFilePath();
762
13
    node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str());
763
13
    return Nothing<URL>();
764
  }
765
766
139
  return Just(resolved);
767
}
768
769
4
Maybe<URL> PackageMainResolve(Environment* env,
770
                              const URL& pjson_url,
771
                              const PackageConfig& pcfg,
772
                              const URL& base) {
773
4
  if (pcfg.exists == Exists::Yes) {
774
4
    if (pcfg.has_main == HasMain::Yes) {
775
4
      URL resolved(pcfg.main, pjson_url);
776
7
      const std::string& path = resolved.ToFilePath();
777
4
      if (CheckDescriptorAtPath(path) == FILE) {
778
1
        return Just(resolved);
779
3
      }
780
    }
781
3
    if (env->options()->es_module_specifier_resolution == "node") {
782
3
      if (pcfg.has_main == HasMain::Yes) {
783
3
        return FinalizeResolution(env, URL(pcfg.main, pjson_url), base);
784
      } else {
785
        return FinalizeResolution(env, URL("index", pjson_url), base);
786
      }
787
    }
788
    if (pcfg.type != PackageType::Module) {
789
      Maybe<URL> resolved = LegacyMainResolve(pjson_url, pcfg);
790
      if (!resolved.IsNothing()) {
791
        return resolved;
792
      }
793
    }
794
  }
795
  std::string msg = "Cannot find main entry point for '" +
796
      URL(".", pjson_url).ToFilePath() + "' imported from " +
797
      base.ToFilePath();
798
  node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str());
799
  return Nothing<URL>();
800
}
801
802
6
Maybe<URL> PackageResolve(Environment* env,
803
                          const std::string& specifier,
804
                          const URL& base) {
805
6
  size_t sep_index = specifier.find('/');
806


6
  if (specifier[0] == '@' && (sep_index == std::string::npos ||
807
      specifier.length() == 0)) {
808
    std::string msg = "Invalid package name '" + specifier +
809
      "' imported from " + base.ToFilePath();
810
    node::THROW_ERR_INVALID_MODULE_SPECIFIER(env, msg.c_str());
811
    return Nothing<URL>();
812
  }
813
6
  bool scope = false;
814
6
  if (specifier[0] == '@') {
815
    scope = true;
816
    sep_index = specifier.find('/', sep_index + 1);
817
  }
818
  std::string pkg_name = specifier.substr(0,
819
6
      sep_index == std::string::npos ? std::string::npos : sep_index);
820
12
  std::string pkg_subpath;
821

6
  if ((sep_index == std::string::npos ||
822
      sep_index == specifier.length() - 1)) {
823
6
    pkg_subpath = "";
824
  } else {
825
    pkg_subpath = "." + specifier.substr(sep_index);
826
  }
827
12
  URL pjson_url("./node_modules/" + pkg_name + "/package.json", &base);
828
12
  std::string pjson_path = pjson_url.ToFilePath();
829
12
  std::string last_path;
830
19
  do {
831
    DescriptorType check =
832
23
        CheckDescriptorAtPath(pjson_path.substr(0, pjson_path.length() - 13));
833
23
    if (check != DIRECTORY) {
834
19
      last_path = pjson_path;
835
38
      pjson_url = URL((scope ?
836
38
          "../../../../node_modules/" : "../../../node_modules/") +
837
57
          pkg_name + "/package.json", &pjson_url);
838
19
      pjson_path = pjson_url.ToFilePath();
839
19
      continue;
840
    }
841
842
    // Package match.
843
4
    Maybe<const PackageConfig*> pcfg = GetPackageConfig(env, pjson_path, base);
844
    // Invalid package configuration error.
845
8
    if (pcfg.IsNothing()) return Nothing<URL>();
846
4
    if (!pkg_subpath.length()) {
847
4
      return PackageMainResolve(env, pjson_url, *pcfg.FromJust(), base);
848
    } else {
849
      return FinalizeResolution(env, URL(pkg_subpath, pjson_url), base);
850
    }
851
    CHECK(false);
852
    // Cross-platform root check.
853
19
  } while (pjson_path.length() != last_path.length());
854
855
4
  std::string msg = "Cannot find package '" + pkg_name +
856
8
      "' imported from " + base.ToFilePath();
857
2
  node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str());
858
8
  return Nothing<URL>();
859
}
860
861
}  // anonymous namespace
862
863
172
Maybe<URL> Resolve(Environment* env,
864
                   const std::string& specifier,
865
                   const URL& base) {
866
  // Order swapped from spec for minor perf gain.
867
  // Ok since relative URLs cannot parse as URLs.
868
172
  URL resolved;
869
172
  if (ShouldBeTreatedAsRelativeOrAbsolutePath(specifier)) {
870
163
    resolved = URL(specifier, base);
871
  } else {
872
9
    URL pure_url(specifier);
873
9
    if (!(pure_url.flags() & URL_FLAGS_FAILED)) {
874
3
      resolved = pure_url;
875
    } else {
876
6
      return PackageResolve(env, specifier, base);
877
3
    }
878
  }
879
166
  return FinalizeResolution(env, resolved, base);
880
}
881
882
172
void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
883
172
  Environment* env = Environment::GetCurrent(args);
884
885
  // module.resolve(specifier, url)
886
172
  CHECK_EQ(args.Length(), 2);
887
888
516
  CHECK(args[0]->IsString());
889
172
  Utf8Value specifier_utf8(env->isolate(), args[0]);
890
328
  std::string specifier_std(*specifier_utf8, specifier_utf8.length());
891
892
516
  CHECK(args[1]->IsString());
893
328
  Utf8Value url_utf8(env->isolate(), args[1]);
894
328
  URL url(*url_utf8, url_utf8.length());
895
896
172
  if (url.flags() & URL_FLAGS_FAILED) {
897
    return node::THROW_ERR_INVALID_ARG_TYPE(
898
        env, "second argument is not a URL string");
899
  }
900
901
  Maybe<URL> result =
902
      node::loader::Resolve(env,
903
                            specifier_std,
904
328
                            url);
905
172
  if (result.IsNothing()) {
906
16
    return;
907
  }
908
909
156
  URL resolution = result.FromJust();
910
156
  CHECK(!(resolution.flags() & URL_FLAGS_FAILED));
911
912
  Local<Value> resolution_obj;
913
312
  if (resolution.ToObject(env).ToLocal(&resolution_obj))
914
468
    args.GetReturnValue().Set(resolution_obj);
915
}
916
917
156
void ModuleWrap::GetPackageType(const FunctionCallbackInfo<Value>& args) {
918
156
  Environment* env = Environment::GetCurrent(args);
919
920
  // module.getPackageType(url)
921
156
  CHECK_EQ(args.Length(), 1);
922
923
468
  CHECK(args[0]->IsString());
924
156
  Utf8Value url_utf8(env->isolate(), args[0]);
925
312
  URL url(*url_utf8, url_utf8.length());
926
927
156
  PackageType pkg_type = PackageType::None;
928
  Maybe<const PackageConfig*> pcfg =
929
156
      GetPackageScopeConfig(env, url, url);
930
156
  if (!pcfg.IsNothing()) {
931
156
    pkg_type = pcfg.FromJust()->type;
932
  }
933
934
624
  args.GetReturnValue().Set(Integer::New(env->isolate(), pkg_type));
935
156
}
936
937
26
static MaybeLocal<Promise> ImportModuleDynamically(
938
    Local<Context> context,
939
    Local<v8::ScriptOrModule> referrer,
940
    Local<String> specifier) {
941
26
  Isolate* iso = context->GetIsolate();
942
26
  Environment* env = Environment::GetCurrent(context);
943
26
  CHECK_NOT_NULL(env);  // TODO(addaleax): Handle nullptr here.
944
26
  v8::EscapableHandleScope handle_scope(iso);
945
946
  Local<Function> import_callback =
947
26
    env->host_import_module_dynamically_callback();
948
949
26
  Local<PrimitiveArray> options = referrer->GetHostDefinedOptions();
950
26
  if (options->Length() != HostDefinedOptions::kLength) {
951
    Local<Promise::Resolver> resolver =
952
        Promise::Resolver::New(context).ToLocalChecked();
953
    resolver
954
        ->Reject(context,
955
                 v8::Exception::TypeError(FIXED_ONE_BYTE_STRING(
956
                     context->GetIsolate(), "Invalid host defined options")))
957
        .ToChecked();
958
    return handle_scope.Escape(resolver->GetPromise());
959
  }
960
961
  Local<Value> object;
962
963
26
  int type = options->Get(iso, HostDefinedOptions::kType)
964
52
                 .As<Number>()
965
78
                 ->Int32Value(context)
966
52
                 .ToChecked();
967
26
  uint32_t id = options->Get(iso, HostDefinedOptions::kID)
968
52
                    .As<Number>()
969
78
                    ->Uint32Value(context)
970
52
                    .ToChecked();
971
26
  if (type == ScriptType::kScript) {
972
2
    contextify::ContextifyScript* wrap = env->id_to_script_map.find(id)->second;
973
4
    object = wrap->object();
974
24
  } else if (type == ScriptType::kModule) {
975
13
    ModuleWrap* wrap = ModuleWrap::GetFromID(env, id);
976
26
    object = wrap->object();
977
11
  } else if (type == ScriptType::kFunction) {
978
22
    object = env->id_to_function_map.find(id)->second.Get(iso);
979
  } else {
980
    UNREACHABLE();
981
  }
982
983
  Local<Value> import_args[] = {
984
    object,
985
    Local<Value>(specifier),
986
52
  };
987
988
  Local<Value> result;
989
52
  if (import_callback->Call(
990
        context,
991
        v8::Undefined(iso),
992
26
        arraysize(import_args),
993
78
        import_args).ToLocal(&result)) {
994
26
    CHECK(result->IsPromise());
995
26
    return handle_scope.Escape(result.As<Promise>());
996
  }
997
998
  return MaybeLocal<Promise>();
999
}
1000
1001
86
void ModuleWrap::SetImportModuleDynamicallyCallback(
1002
    const FunctionCallbackInfo<Value>& args) {
1003
86
  Isolate* iso = args.GetIsolate();
1004
86
  Environment* env = Environment::GetCurrent(args);
1005
86
  HandleScope handle_scope(iso);
1006
1007
86
  CHECK_EQ(args.Length(), 1);
1008
172
  CHECK(args[0]->IsFunction());
1009
172
  Local<Function> import_callback = args[0].As<Function>();
1010
86
  env->set_host_import_module_dynamically_callback(import_callback);
1011
1012
86
  iso->SetHostImportModuleDynamicallyCallback(ImportModuleDynamically);
1013
86
}
1014
1015
161
void ModuleWrap::HostInitializeImportMetaObjectCallback(
1016
    Local<Context> context, Local<Module> module, Local<Object> meta) {
1017
161
  Environment* env = Environment::GetCurrent(context);
1018
161
  CHECK_NOT_NULL(env);  // TODO(addaleax): Handle nullptr here.
1019
161
  ModuleWrap* module_wrap = GetFromModule(env, module);
1020
1021
161
  if (module_wrap == nullptr) {
1022
161
    return;
1023
  }
1024
1025
161
  Local<Object> wrap = module_wrap->object();
1026
  Local<Function> callback =
1027
161
      env->host_initialize_import_meta_object_callback();
1028
483
  Local<Value> args[] = { wrap, meta };
1029
483
  callback->Call(context, Undefined(env->isolate()), arraysize(args), args)
1030
322
      .ToLocalChecked();
1031
}
1032
1033
86
void ModuleWrap::SetInitializeImportMetaObjectCallback(
1034
    const FunctionCallbackInfo<Value>& args) {
1035
86
  Environment* env = Environment::GetCurrent(args);
1036
86
  Isolate* isolate = env->isolate();
1037
1038
86
  CHECK_EQ(args.Length(), 1);
1039
172
  CHECK(args[0]->IsFunction());
1040
172
  Local<Function> import_meta_callback = args[0].As<Function>();
1041
86
  env->set_host_initialize_import_meta_object_callback(import_meta_callback);
1042
1043
  isolate->SetHostInitializeImportMetaObjectCallback(
1044
86
      HostInitializeImportMetaObjectCallback);
1045
86
}
1046
1047
4636
void ModuleWrap::Initialize(Local<Object> target,
1048
                            Local<Value> unused,
1049
                            Local<Context> context,
1050
                            void* priv) {
1051
4636
  Environment* env = Environment::GetCurrent(context);
1052
4636
  Isolate* isolate = env->isolate();
1053
1054
4636
  Local<FunctionTemplate> tpl = env->NewFunctionTemplate(New);
1055
9272
  tpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "ModuleWrap"));
1056
9272
  tpl->InstanceTemplate()->SetInternalFieldCount(1);
1057
1058
4636
  env->SetProtoMethod(tpl, "link", Link);
1059
4636
  env->SetProtoMethod(tpl, "instantiate", Instantiate);
1060
4636
  env->SetProtoMethod(tpl, "evaluate", Evaluate);
1061
4636
  env->SetProtoMethodNoSideEffect(tpl, "namespace", Namespace);
1062
4636
  env->SetProtoMethodNoSideEffect(tpl, "getStatus", GetStatus);
1063
4636
  env->SetProtoMethodNoSideEffect(tpl, "getError", GetError);
1064
  env->SetProtoMethodNoSideEffect(tpl, "getStaticDependencySpecifiers",
1065
4636
                                  GetStaticDependencySpecifiers);
1066
1067
  target->Set(env->context(), FIXED_ONE_BYTE_STRING(isolate, "ModuleWrap"),
1068
23180
              tpl->GetFunction(context).ToLocalChecked()).Check();
1069
4636
  env->SetMethod(target, "resolve", Resolve);
1070
4636
  env->SetMethod(target, "getPackageType", GetPackageType);
1071
  env->SetMethod(target,
1072
                 "setImportModuleDynamicallyCallback",
1073
4636
                 SetImportModuleDynamicallyCallback);
1074
  env->SetMethod(target,
1075
                 "setInitializeImportMetaObjectCallback",
1076
4636
                 SetInitializeImportMetaObjectCallback);
1077
1078
#define V(name)                                                                \
1079
    target->Set(context,                                                       \
1080
      FIXED_ONE_BYTE_STRING(env->isolate(), #name),                            \
1081
      Integer::New(env->isolate(), Module::Status::name))                      \
1082
        .FromJust()
1083
18544
    V(kUninstantiated);
1084
18544
    V(kInstantiating);
1085
18544
    V(kInstantiated);
1086
18544
    V(kEvaluating);
1087
18544
    V(kEvaluated);
1088
18544
    V(kErrored);
1089
#undef V
1090
4636
}
1091
1092
}  // namespace loader
1093
}  // namespace node
1094
1095
4523
NODE_MODULE_CONTEXT_AWARE_INTERNAL(module_wrap,
1096
                                   node::loader::ModuleWrap::Initialize)