Node.js  v8.x
Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine
module_wrap.cc
Go to the documentation of this file.
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_url.h"
8 #include "util.h"
9 #include "util-inl.h"
10 
11 namespace node {
12 namespace loader {
13 
14 using node::url::URL;
15 using node::url::URL_FLAGS_FAILED;
16 using v8::Context;
17 using v8::EscapableHandleScope;
18 using v8::Function;
19 using v8::FunctionCallbackInfo;
20 using v8::FunctionTemplate;
21 using v8::Integer;
22 using v8::IntegrityLevel;
23 using v8::Isolate;
24 using v8::JSON;
25 using v8::Local;
26 using v8::MaybeLocal;
27 using v8::Module;
28 using v8::Object;
29 using v8::Persistent;
30 using v8::Promise;
31 using v8::ScriptCompiler;
32 using v8::ScriptOrigin;
33 using v8::String;
34 using v8::Value;
35 
36 static const char* EXTENSIONS[] = {".mjs", ".js", ".json", ".node"};
37 std::map<int, std::vector<ModuleWrap*>*> ModuleWrap::module_map_;
38 
39 ModuleWrap::ModuleWrap(Environment* env,
40  Local<Object> object,
41  Local<Module> module,
42  Local<String> url) : BaseObject(env, object) {
43  Isolate* iso = Isolate::GetCurrent();
44  module_.Reset(iso, module);
45  url_.Reset(iso, url);
46 }
47 
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);
52 
53  if (it != same_hash->end()) {
54  same_hash->erase(it);
55  }
56 
57  module_.Reset();
58 }
59 
60 void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
61  Environment* env = Environment::GetCurrent(args);
62 
63  Isolate* iso = args.GetIsolate();
64 
65  if (!args.IsConstructCall()) {
66  env->ThrowError("constructor must be called using new");
67  return;
68  }
69 
70  if (args.Length() != 2) {
71  env->ThrowError("constructor must have exactly 2 arguments "
72  "(string, string)");
73  return;
74  }
75 
76  if (!args[0]->IsString()) {
77  env->ThrowError("first argument is not a string");
78  return;
79  }
80 
81  auto source_text = args[0].As<String>();
82 
83  if (!args[1]->IsString()) {
84  env->ThrowError("second argument is not a string");
85  return;
86  }
87 
88  Local<String> url = args[1].As<String>();
89 
90  Local<Module> mod;
91 
92  // compile
93  {
94  ScriptOrigin origin(url,
95  Integer::New(iso, 0),
96  Integer::New(iso, 0),
97  False(iso),
98  Integer::New(iso, 0),
99  FIXED_ONE_BYTE_STRING(iso, ""),
100  False(iso),
101  False(iso),
102  True(iso));
103  ScriptCompiler::Source source(source_text, origin);
104  auto maybe_mod = ScriptCompiler::CompileModule(iso, &source);
105  if (maybe_mod.IsEmpty()) {
106  return;
107  }
108  mod = maybe_mod.ToLocalChecked();
109  }
110 
111  auto that = args.This();
112  auto ctx = that->CreationContext();
113  auto url_str = FIXED_ONE_BYTE_STRING(iso, "url");
114 
115  if (!that->Set(ctx, url_str, url).FromMaybe(false)) {
116  return;
117  }
118 
119  ModuleWrap* obj =
120  new ModuleWrap(Environment::GetCurrent(ctx), that, mod, url);
121 
122  if (ModuleWrap::module_map_.count(mod->GetIdentityHash()) == 0) {
123  ModuleWrap::module_map_[mod->GetIdentityHash()] =
124  new std::vector<ModuleWrap*>();
125  }
126 
127  ModuleWrap::module_map_[mod->GetIdentityHash()]->push_back(obj);
128  Wrap(that, obj);
129 
130  that->SetIntegrityLevel(ctx, IntegrityLevel::kFrozen);
131  args.GetReturnValue().Set(that);
132 }
133 
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");
140  return;
141  }
142 
143  Local<Function> resolver_arg = args[0].As<Function>();
144 
145  auto that = args.This();
146  ModuleWrap* obj = Unwrap<ModuleWrap>(that);
147  auto mod_context = that->CreationContext();
148  if (obj->linked_) return;
149  obj->linked_ = true;
150  Local<Module> mod(obj->module_.Get(iso));
151 
152  // call the dependency resolve callbacks
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());
157 
158  Local<Value> argv[] = {
159  specifier
160  };
161 
162  MaybeLocal<Value> maybe_resolve_return_value =
163  resolver_arg->Call(mod_context, that, 1, argv);
164  if (maybe_resolve_return_value.IsEmpty()) {
165  return;
166  }
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");
171  }
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);
175  }
176 
177  args.GetReturnValue().Set(handle_scope.Escape(that));
178 }
179 
180 void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) {
181  auto iso = args.GetIsolate();
182  auto that = args.This();
183  auto ctx = that->CreationContext();
184 
185  ModuleWrap* obj = Unwrap<ModuleWrap>(that);
186  Local<Module> mod = obj->module_.Get(iso);
187  bool ok = mod->Instantiate(ctx, ModuleWrap::ResolveCallback);
188 
189  // clear resolve cache on instantiate
190  obj->resolve_cache_.clear();
191 
192  if (!ok) {
193  return;
194  }
195 }
196 
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);
203 
204  if (result.IsEmpty()) {
205  return;
206  }
207 
208  auto ret = result.ToLocalChecked();
209  args.GetReturnValue().Set(ret);
210 }
211 
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>();
220  }
221 
222  std::vector<ModuleWrap*>* possible_deps =
223  ModuleWrap::module_map_[referrer->GetIdentityHash()];
224  ModuleWrap* dependent = nullptr;
225 
226  for (auto possible_dep : *possible_deps) {
227  if (possible_dep->module_ == referrer) {
228  dependent = possible_dep;
229  }
230  }
231 
232  if (dependent == nullptr) {
233  env->ThrowError("linking error, null dep");
234  return MaybeLocal<Module>();
235  }
236 
237  Utf8Value specifier_utf(env->isolate(), specifier);
238  std::string specifier_std(*specifier_utf, specifier_utf.length());
239 
240  if (dependent->resolve_cache_.count(specifier_std) != 1) {
241  env->ThrowError("linking error, not in local cache");
242  return MaybeLocal<Module>();
243  }
244 
245  Local<Promise> resolve_promise =
246  dependent->resolve_cache_[specifier_std]->Get(iso);
247 
248  if (resolve_promise->State() != Promise::kFulfilled) {
249  env->ThrowError("linking error, dependency promises must be resolved on "
250  "instantiate");
251  return MaybeLocal<Module>();
252  }
253 
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 "
257  "resolver");
258  return MaybeLocal<Module>();
259  }
260 
261  ModuleWrap* mod;
262  ASSIGN_OR_RETURN_UNWRAP(&mod, module_object, MaybeLocal<Module>());
263  return mod->module_.Get(env->isolate());
264 }
265 
266 namespace {
267 
268 URL __init_cwd() {
269  std::string specifier = "file://";
270 #ifdef _WIN32
271  // MAX_PATH is in characters, not bytes. Make sure we have enough headroom.
272  char buf[MAX_PATH * 4];
273 #else
274  char buf[PATH_MAX];
275 #endif
276 
277  size_t cwd_len = sizeof(buf);
278  int err = uv_cwd(buf, &cwd_len);
279  if (err) {
280  return URL("");
281  }
282  specifier += buf;
283  specifier += "/";
284  return URL(specifier);
285 }
286 static URL INITIAL_CWD(__init_cwd());
287 inline bool is_relative_or_absolute_path(std::string specifier) {
288  auto len = specifier.length();
289  if (len <= 0) {
290  return false;
291  } else if (specifier[0] == '/') {
292  return true;
293  } else if (specifier[0] == '.') {
294  if (len == 1 || specifier[1] == '/') {
295  return true;
296  } else if (specifier[1] == '.') {
297  if (len == 2 || specifier[2] == '/') {
298  return true;
299  }
300  }
301  }
302  return false;
303 }
304 struct read_result {
305  bool had_error = false;
306  std::string source;
307 } read_result;
308 inline const struct read_result read_file(uv_file file) {
309  struct read_result ret;
310  std::string src;
311  uv_fs_t req;
312  void* base = malloc(4096);
313  if (base == nullptr) {
314  ret.had_error = true;
315  return ret;
316  }
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);
322  }
323  ret.source = src;
324  return ret;
325 }
326 struct file_check {
327  bool failed = true;
328  uv_file file;
329 } file_check;
330 inline const struct file_check check_file(URL search,
331  bool close = false,
332  bool allow_dir = false) {
333  struct file_check ret;
334  uv_fs_t fs_req;
335  std::string path = search.ToFilePath();
336  if (path.empty()) {
337  return ret;
338  }
339  uv_fs_open(nullptr, &fs_req, path.c_str(), O_RDONLY, 0, nullptr);
340  auto fd = fs_req.result;
341  if (fd < 0) {
342  return ret;
343  }
344  if (!allow_dir) {
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);
348  return ret;
349  }
350  }
351  ret.failed = false;
352  ret.file = fd;
353  if (close) uv_fs_close(nullptr, &fs_req, fd, nullptr);
354  return ret;
355 }
356 URL resolve_extensions(URL search, bool check_exact = true) {
357  if (check_exact) {
358  auto check = check_file(search, true);
359  if (!check.failed) {
360  return search;
361  }
362  }
363  for (auto extension : EXTENSIONS) {
364  URL guess(search.path() + extension, &search);
365  auto check = check_file(guess, true);
366  if (!check.failed) {
367  return guess;
368  }
369  }
370  return URL("");
371 }
372 inline URL resolve_index(URL search) {
373  return resolve_extensions(URL("index", &search), false);
374 }
375 URL resolve_main(URL search) {
376  URL pkg("package.json", &search);
377  auto check = check_file(pkg);
378  if (!check.failed) {
379  auto iso = Isolate::GetCurrent();
380  auto ctx = iso->GetCurrentContext();
381  auto read = read_file(check.file);
382  uv_fs_t fs_req;
383  // if we fail to close :-/
384  uv_fs_close(nullptr, &fs_req, check.file, nullptr);
385  if (read.had_error) return URL("");
386  std::string pkg_src = read.source;
387  Local<String> src =
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, "./");
404  }
405  return Resolve(main_std, &search);
406  }
407  return URL("");
408 }
409 URL resolve_module(std::string specifier, URL* base) {
410  URL parent(".", base);
411  URL dir("");
412  do {
413  dir = parent;
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 ?
418  specifier.length() :
419  limit + 1;
420  std::string chroot =
421  dir.path() + "node_modules/" + specifier.substr(0, spec_len);
422  if (check.path().substr(0, chroot.length()) != chroot) {
423  return URL("");
424  }
425  return check;
426  } else {
427  // TODO(bmeck) PREVENT FALLTHROUGH
428  }
429  parent = URL("..", &dir);
430  } while (parent.path() != dir.path());
431  return URL("");
432 }
433 
434 URL resolve_directory(URL search, bool read_pkg_json) {
435  if (read_pkg_json) {
436  auto main = resolve_main(search);
437  if (!(main.flags() & URL_FLAGS_FAILED)) return main;
438  }
439  return resolve_index(search);
440 }
441 
442 } // anonymous namespace
443 
444 
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)) {
448  return pure_url;
449  }
450  if (specifier.length() == 0) {
451  return URL("");
452  }
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);
459  }
460  return resolve_directory(resolved, read_pkg_json);
461  } else {
462  return resolve_module(specifier, base);
463  }
464  return URL("");
465 }
466 
467 void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
468  Environment* env = Environment::GetCurrent(args);
469 
470  if (args.IsConstructCall()) {
471  env->ThrowError("resolve() must not be called as a constructor");
472  return;
473  }
474  if (args.Length() != 2) {
475  env->ThrowError("resolve must have exactly 2 arguments (string, string)");
476  return;
477  }
478 
479  if (!args[0]->IsString()) {
480  env->ThrowError("first argument is not a string");
481  return;
482  }
483  Utf8Value specifier_utf(env->isolate(), args[0]);
484 
485  if (!args[1]->IsString()) {
486  env->ThrowError("second argument is not a string");
487  return;
488  }
489  Utf8Value url_utf(env->isolate(), args[1]);
490  URL url(*url_utf, url_utf.length());
491 
492  if (url.flags() & URL_FLAGS_FAILED) {
493  env->ThrowError("second argument is not a URL string");
494  return;
495  }
496 
497  URL result = node::loader::Resolve(*specifier_utf, &url, true);
498  if (result.flags() & URL_FLAGS_FAILED) {
499  std::string msg = "module ";
500  msg += *specifier_utf;
501  msg += " not found";
502  env->ThrowError(msg.c_str());
503  return;
504  }
505 
506  args.GetReturnValue().Set(result.ToObject(env));
507 }
508 
509 void ModuleWrap::Initialize(Local<Object> target,
510  Local<Value> unused,
511  Local<Context> context) {
512  Environment* env = Environment::GetCurrent(context);
513  Isolate* isolate = env->isolate();
514 
515  Local<FunctionTemplate> tpl = env->NewFunctionTemplate(New);
516  tpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "ModuleWrap"));
517  tpl->InstanceTemplate()->SetInternalFieldCount(1);
518 
519  env->SetProtoMethod(tpl, "link", Link);
520  env->SetProtoMethod(tpl, "instantiate", Instantiate);
521  env->SetProtoMethod(tpl, "evaluate", Evaluate);
522 
523  target->Set(FIXED_ONE_BYTE_STRING(isolate, "ModuleWrap"), tpl->GetFunction());
524  env->SetMethod(target, "resolve", node::loader::ModuleWrap::Resolve);
525 }
526 
527 } // namespace loader
528 } // namespace node
529 
uv_file file
Definition: module_wrap.cc:328
unsigned char * buf
Definition: cares_wrap.cc:483
NODE_MODULE_CONTEXT_AWARE_BUILTIN(inspector, node::inspector::Agent::InitInspector)
int len
Definition: cares_wrap.cc:485
std::string source
Definition: module_wrap.cc:306
int main(int argc, char *argv[])
Definition: node_main.cc:88
bool had_error
Definition: module_wrap.cc:305
bool failed
Definition: module_wrap.cc:327
void Initialize(Local< Object > target, Local< Value > unused, Local< Context > context, void *priv)
Definition: node_http2.cc:1172
uv_fs_t req
Definition: node_file.cc:374
MaybeLocal< Object > New(Isolate *isolate, Local< String > string, enum encoding enc)
Definition: node_buffer.cc:241
StringPtr url_
this ctx
Definition: v8ustack.d:369
URL Resolve(std::string specifier, URL *base, bool read_pkg_json)
Definition: module_wrap.cc:445