#include #include // PATH_MAX #include // S_IFDIR #include "module_wrap.h" #include "env.h" #include "node_errors.h" #include "node_url.h" #include "util-inl.h" #include "node_contextify.h" #include "node_watchdog.h" namespace node { namespace loader { using errors::TryCatchScope; using node::contextify::ContextifyContext; using node::url::URL; using node::url::URL_FLAGS_FAILED; using v8::Array; using v8::Context; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::HandleScope; using v8::Integer; using v8::IntegrityLevel; using v8::Isolate; using v8::JSON; using v8::Just; using v8::Local; using v8::Maybe; using v8::MaybeLocal; using v8::Module; using v8::Nothing; using v8::Number; using v8::Object; using v8::PrimitiveArray; using v8::Promise; using v8::ScriptCompiler; using v8::ScriptOrigin; using v8::String; using v8::Undefined; using v8::Value; static const char* const EXTENSIONS[] = {".mjs", ".js", ".json", ".node"}; ModuleWrap::ModuleWrap(Environment* env, Local object, Local module, Local url) : BaseObject(env, object), id_(env->get_next_module_id()) { module_.Reset(env->isolate(), module); url_.Reset(env->isolate(), url); env->id_to_module_map.emplace(id_, this); } ModuleWrap::~ModuleWrap() { HandleScope scope(env()->isolate()); Local module = module_.Get(env()->isolate()); env()->id_to_module_map.erase(id_); auto range = env()->hash_to_module_map.equal_range(module->GetIdentityHash()); for (auto it = range.first; it != range.second; ++it) { if (it->second == this) { env()->hash_to_module_map.erase(it); break; } } } ModuleWrap* ModuleWrap::GetFromModule(Environment* env, Local module) { auto range = env->hash_to_module_map.equal_range(module->GetIdentityHash()); for (auto it = range.first; it != range.second; ++it) { if (it->second->module_ == module) { return it->second; } } return nullptr; } ModuleWrap* ModuleWrap::GetFromID(Environment* env, uint32_t id) { auto module_wrap_it = env->id_to_module_map.find(id); if (module_wrap_it == env->id_to_module_map.end()) { return nullptr; } return module_wrap_it->second; } void ModuleWrap::New(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); CHECK(args.IsConstructCall()); Local that = args.This(); const int argc = args.Length(); CHECK_GE(argc, 2); CHECK(args[0]->IsString()); Local source_text = args[0].As(); CHECK(args[1]->IsString()); Local url = args[1].As(); Local context; Local line_offset; Local column_offset; if (argc == 5) { // new ModuleWrap(source, url, context?, lineOffset, columnOffset) if (args[2]->IsUndefined()) { context = that->CreationContext(); } else { CHECK(args[2]->IsObject()); ContextifyContext* sandbox = ContextifyContext::ContextFromContextifiedSandbox( env, args[2].As()); CHECK_NOT_NULL(sandbox); context = sandbox->context(); } CHECK(args[3]->IsNumber()); line_offset = args[3].As(); CHECK(args[4]->IsNumber()); column_offset = args[4].As(); } else { // new ModuleWrap(source, url) context = that->CreationContext(); line_offset = Integer::New(isolate, 0); column_offset = Integer::New(isolate, 0); } Environment::ShouldNotAbortOnUncaughtScope no_abort_scope(env); TryCatchScope try_catch(env); Local module; Local host_defined_options = PrimitiveArray::New(isolate, HostDefinedOptions::kLength); host_defined_options->Set(isolate, HostDefinedOptions::kType, Number::New(isolate, ScriptType::kModule)); // compile { ScriptOrigin origin(url, line_offset, // line offset column_offset, // column offset True(isolate), // is cross origin Local(), // script id Local(), // source map URL False(isolate), // is opaque (?) False(isolate), // is WASM True(isolate), // is ES Module host_defined_options); Context::Scope context_scope(context); ScriptCompiler::Source source(source_text, origin); if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) { CHECK(try_catch.HasCaught()); CHECK(!try_catch.Message().IsEmpty()); CHECK(!try_catch.Exception().IsEmpty()); AppendExceptionLine(env, try_catch.Exception(), try_catch.Message(), ErrorHandlingMode::MODULE_ERROR); try_catch.ReThrow(); return; } } if (!that->Set(context, env->url_string(), url).FromMaybe(false)) { return; } ModuleWrap* obj = new ModuleWrap(env, that, module, url); obj->context_.Reset(isolate, context); env->hash_to_module_map.emplace(module->GetIdentityHash(), obj); host_defined_options->Set(isolate, HostDefinedOptions::kID, Number::New(isolate, obj->id())); that->SetIntegrityLevel(context, IntegrityLevel::kFrozen); args.GetReturnValue().Set(that); } void ModuleWrap::Link(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = args.GetIsolate(); CHECK_EQ(args.Length(), 1); CHECK(args[0]->IsFunction()); Local that = args.This(); ModuleWrap* obj; ASSIGN_OR_RETURN_UNWRAP(&obj, that); if (obj->linked_) return; obj->linked_ = true; Local resolver_arg = args[0].As(); Local mod_context = obj->context_.Get(isolate); Local module = obj->module_.Get(isolate); Local promises = Array::New(isolate, module->GetModuleRequestsLength()); // call the dependency resolve callbacks for (int i = 0; i < module->GetModuleRequestsLength(); i++) { Local specifier = module->GetModuleRequest(i); Utf8Value specifier_utf8(env->isolate(), specifier); std::string specifier_std(*specifier_utf8, specifier_utf8.length()); Local argv[] = { specifier }; MaybeLocal maybe_resolve_return_value = resolver_arg->Call(mod_context, that, 1, argv); if (maybe_resolve_return_value.IsEmpty()) { return; } Local resolve_return_value = maybe_resolve_return_value.ToLocalChecked(); if (!resolve_return_value->IsPromise()) { env->ThrowError("linking error, expected resolver to return a promise"); } Local resolve_promise = resolve_return_value.As(); obj->resolve_cache_[specifier_std].Reset(env->isolate(), resolve_promise); promises->Set(mod_context, i, resolve_promise).FromJust(); } args.GetReturnValue().Set(promises); } void ModuleWrap::Instantiate(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = args.GetIsolate(); ModuleWrap* obj; ASSIGN_OR_RETURN_UNWRAP(&obj, args.This()); Local context = obj->context_.Get(isolate); Local module = obj->module_.Get(isolate); TryCatchScope try_catch(env); Maybe ok = module->InstantiateModule(context, ResolveCallback); // clear resolve cache on instantiate obj->resolve_cache_.clear(); if (!ok.FromMaybe(false)) { CHECK(try_catch.HasCaught()); CHECK(!try_catch.Message().IsEmpty()); CHECK(!try_catch.Exception().IsEmpty()); AppendExceptionLine(env, try_catch.Exception(), try_catch.Message(), ErrorHandlingMode::MODULE_ERROR); try_catch.ReThrow(); return; } } void ModuleWrap::Evaluate(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); ModuleWrap* obj; ASSIGN_OR_RETURN_UNWRAP(&obj, args.This()); Local context = obj->context_.Get(isolate); Local module = obj->module_.Get(isolate); // module.evaluate(timeout, breakOnSigint) CHECK_EQ(args.Length(), 2); CHECK(args[0]->IsNumber()); int64_t timeout = args[0]->IntegerValue(env->context()).FromJust(); CHECK(args[1]->IsBoolean()); bool break_on_sigint = args[1]->IsTrue(); Environment::ShouldNotAbortOnUncaughtScope no_abort_scope(env); TryCatchScope try_catch(env); bool timed_out = false; bool received_signal = false; MaybeLocal result; if (break_on_sigint && timeout != -1) { Watchdog wd(isolate, timeout, &timed_out); SigintWatchdog swd(isolate, &received_signal); result = module->Evaluate(context); } else if (break_on_sigint) { SigintWatchdog swd(isolate, &received_signal); result = module->Evaluate(context); } else if (timeout != -1) { Watchdog wd(isolate, timeout, &timed_out); result = module->Evaluate(context); } else { result = module->Evaluate(context); } // Convert the termination exception into a regular exception. if (timed_out || received_signal) { env->isolate()->CancelTerminateExecution(); // It is possible that execution was terminated by another timeout in // which this timeout is nested, so check whether one of the watchdogs // from this invocation is responsible for termination. if (timed_out) { THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(env, timeout); } else if (received_signal) { THROW_ERR_SCRIPT_EXECUTION_INTERRUPTED(env); } } if (try_catch.HasCaught()) { try_catch.ReThrow(); return; } args.GetReturnValue().Set(result.ToLocalChecked()); } void ModuleWrap::Namespace(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = args.GetIsolate(); ModuleWrap* obj; ASSIGN_OR_RETURN_UNWRAP(&obj, args.This()); Local module = obj->module_.Get(isolate); switch (module->GetStatus()) { default: return env->ThrowError( "cannot get namespace, Module has not been instantiated"); case v8::Module::Status::kInstantiated: case v8::Module::Status::kEvaluating: case v8::Module::Status::kEvaluated: break; } Local result = module->GetModuleNamespace(); args.GetReturnValue().Set(result); } void ModuleWrap::GetStatus(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); ModuleWrap* obj; ASSIGN_OR_RETURN_UNWRAP(&obj, args.This()); Local module = obj->module_.Get(isolate); args.GetReturnValue().Set(module->GetStatus()); } void ModuleWrap::GetStaticDependencySpecifiers( const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); ModuleWrap* obj; ASSIGN_OR_RETURN_UNWRAP(&obj, args.This()); Local module = obj->module_.Get(env->isolate()); int count = module->GetModuleRequestsLength(); Local specifiers = Array::New(env->isolate(), count); for (int i = 0; i < count; i++) specifiers->Set(env->context(), i, module->GetModuleRequest(i)).FromJust(); args.GetReturnValue().Set(specifiers); } void ModuleWrap::GetError(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); ModuleWrap* obj; ASSIGN_OR_RETURN_UNWRAP(&obj, args.This()); Local module = obj->module_.Get(isolate); args.GetReturnValue().Set(module->GetException()); } MaybeLocal ModuleWrap::ResolveCallback(Local context, Local specifier, Local referrer) { Environment* env = Environment::GetCurrent(context); CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here. Isolate* isolate = env->isolate(); ModuleWrap* dependent = GetFromModule(env, referrer); if (dependent == nullptr) { env->ThrowError("linking error, null dep"); return MaybeLocal(); } Utf8Value specifier_utf8(isolate, specifier); std::string specifier_std(*specifier_utf8, specifier_utf8.length()); if (dependent->resolve_cache_.count(specifier_std) != 1) { env->ThrowError("linking error, not in local cache"); return MaybeLocal(); } Local resolve_promise = dependent->resolve_cache_[specifier_std].Get(isolate); if (resolve_promise->State() != Promise::kFulfilled) { env->ThrowError("linking error, dependency promises must be resolved on " "instantiate"); return MaybeLocal(); } Local module_object = resolve_promise->Result().As(); if (module_object.IsEmpty() || !module_object->IsObject()) { env->ThrowError("linking error, expected a valid module object from " "resolver"); return MaybeLocal(); } ModuleWrap* module; ASSIGN_OR_RETURN_UNWRAP(&module, module_object, MaybeLocal()); return module->module_.Get(isolate); } namespace { // Tests whether a path starts with /, ./ or ../ // In WhatWG terminology, the alternative case is called a "bare" specifier // (e.g. in `import "jquery"`). inline bool ShouldBeTreatedAsRelativeOrAbsolutePath( const std::string& specifier) { size_t len = specifier.length(); if (len == 0) return false; if (specifier[0] == '/') { return true; } else if (specifier[0] == '.') { if (len == 1 || specifier[1] == '/') return true; if (specifier[1] == '.') { if (len == 2 || specifier[2] == '/') return true; } } return false; } std::string ReadFile(uv_file file) { std::string contents; uv_fs_t req; char buffer_memory[4096]; uv_buf_t buf = uv_buf_init(buffer_memory, sizeof(buffer_memory)); do { const int r = uv_fs_read(uv_default_loop(), &req, file, &buf, 1, contents.length(), // offset nullptr); uv_fs_req_cleanup(&req); if (r <= 0) break; contents.append(buf.base, r); } while (true); return contents; } enum CheckFileOptions { LEAVE_OPEN_AFTER_CHECK, CLOSE_AFTER_CHECK }; Maybe CheckFile(const std::string& path, CheckFileOptions opt = CLOSE_AFTER_CHECK) { uv_fs_t fs_req; if (path.empty()) { return Nothing(); } uv_file fd = uv_fs_open(nullptr, &fs_req, path.c_str(), O_RDONLY, 0, nullptr); uv_fs_req_cleanup(&fs_req); if (fd < 0) { return Nothing(); } uv_fs_fstat(nullptr, &fs_req, fd, nullptr); uint64_t is_directory = fs_req.statbuf.st_mode & S_IFDIR; uv_fs_req_cleanup(&fs_req); if (is_directory) { CHECK_EQ(0, uv_fs_close(nullptr, &fs_req, fd, nullptr)); uv_fs_req_cleanup(&fs_req); return Nothing(); } if (opt == CLOSE_AFTER_CHECK) { CHECK_EQ(0, uv_fs_close(nullptr, &fs_req, fd, nullptr)); uv_fs_req_cleanup(&fs_req); } return Just(fd); } using Exists = PackageConfig::Exists; using IsValid = PackageConfig::IsValid; using HasMain = PackageConfig::HasMain; const PackageConfig& GetPackageConfig(Environment* env, const std::string& path) { auto existing = env->package_json_cache.find(path); if (existing != env->package_json_cache.end()) { return existing->second; } Maybe check = CheckFile(path, LEAVE_OPEN_AFTER_CHECK); if (check.IsNothing()) { auto entry = env->package_json_cache.emplace(path, PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "" }); return entry.first->second; } Isolate* isolate = env->isolate(); v8::HandleScope handle_scope(isolate); std::string pkg_src = ReadFile(check.FromJust()); uv_fs_t fs_req; CHECK_EQ(0, uv_fs_close(nullptr, &fs_req, check.FromJust(), nullptr)); uv_fs_req_cleanup(&fs_req); Local src; if (!String::NewFromUtf8(isolate, pkg_src.c_str(), v8::NewStringType::kNormal, pkg_src.length()).ToLocal(&src)) { auto entry = env->package_json_cache.emplace(path, PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "" }); return entry.first->second; } Local pkg_json_v; Local pkg_json; if (!JSON::Parse(env->context(), src).ToLocal(&pkg_json_v) || !pkg_json_v->ToObject(env->context()).ToLocal(&pkg_json)) { auto entry = env->package_json_cache.emplace(path, PackageConfig { Exists::Yes, IsValid::No, HasMain::No, "" }); return entry.first->second; } Local pkg_main; HasMain has_main = HasMain::No; std::string main_std; if (pkg_json->Get(env->context(), env->main_string()).ToLocal(&pkg_main)) { has_main = HasMain::Yes; Utf8Value main_utf8(isolate, pkg_main); main_std.assign(std::string(*main_utf8, main_utf8.length())); } auto entry = env->package_json_cache.emplace(path, PackageConfig { Exists::Yes, IsValid::Yes, has_main, main_std }); return entry.first->second; } enum ResolveExtensionsOptions { TRY_EXACT_NAME, ONLY_VIA_EXTENSIONS }; template Maybe ResolveExtensions(const URL& search) { if (options == TRY_EXACT_NAME) { std::string filePath = search.ToFilePath(); Maybe check = CheckFile(filePath); if (!check.IsNothing()) { return Just(search); } } for (const char* extension : EXTENSIONS) { URL guess(search.path() + extension, &search); Maybe check = CheckFile(guess.ToFilePath()); if (!check.IsNothing()) { return Just(guess); } } return Nothing(); } inline Maybe ResolveIndex(const URL& search) { return ResolveExtensions(URL("index", search)); } Maybe ResolveMain(Environment* env, const URL& search) { URL pkg("package.json", &search); const PackageConfig& pjson = GetPackageConfig(env, pkg.ToFilePath()); // Note invalid package.json should throw in resolver // currently we silently ignore which is incorrect if (pjson.exists == Exists::No || pjson.is_valid == IsValid::No || pjson.has_main == HasMain::No) { return Nothing(); } if (!ShouldBeTreatedAsRelativeOrAbsolutePath(pjson.main)) { return Resolve(env, "./" + pjson.main, search, IgnoreMain); } return Resolve(env, pjson.main, search, IgnoreMain); } Maybe ResolveModule(Environment* env, const std::string& specifier, const URL& base) { URL parent(".", base); URL dir(""); do { dir = parent; Maybe check = Resolve(env, "./node_modules/" + specifier, dir, CheckMain); if (!check.IsNothing()) { const size_t limit = specifier.find('/'); const size_t spec_len = limit == std::string::npos ? specifier.length() : limit + 1; std::string chroot = dir.path() + "node_modules/" + specifier.substr(0, spec_len); if (check.FromJust().path().substr(0, chroot.length()) != chroot) { return Nothing(); } return check; } else { // TODO(bmeck) PREVENT FALLTHROUGH } parent = URL("..", &dir); } while (parent.path() != dir.path()); return Nothing(); } Maybe ResolveDirectory(Environment* env, const URL& search, PackageMainCheck check_pjson_main) { if (check_pjson_main) { Maybe main = ResolveMain(env, search); if (!main.IsNothing()) return main; } return ResolveIndex(search); } } // anonymous namespace Maybe Resolve(Environment* env, const std::string& specifier, const URL& base, PackageMainCheck check_pjson_main) { URL pure_url(specifier); if (!(pure_url.flags() & URL_FLAGS_FAILED)) { // just check existence, without altering Maybe check = CheckFile(pure_url.ToFilePath()); if (check.IsNothing()) { return Nothing(); } return Just(pure_url); } if (specifier.length() == 0) { return Nothing(); } if (ShouldBeTreatedAsRelativeOrAbsolutePath(specifier)) { URL resolved(specifier, base); Maybe file = ResolveExtensions(resolved); if (!file.IsNothing()) return file; if (specifier.back() != '/') { resolved = URL(specifier + "/", base); } return ResolveDirectory(env, resolved, check_pjson_main); } else { return ResolveModule(env, specifier, base); } } void ModuleWrap::Resolve(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); // module.resolve(specifier, url) CHECK_EQ(args.Length(), 2); CHECK(args[0]->IsString()); Utf8Value specifier_utf8(env->isolate(), args[0]); std::string specifier_std(*specifier_utf8, specifier_utf8.length()); CHECK(args[1]->IsString()); Utf8Value url_utf8(env->isolate(), args[1]); URL url(*url_utf8, url_utf8.length()); if (url.flags() & URL_FLAGS_FAILED) { return node::THROW_ERR_INVALID_ARG_TYPE( env, "second argument is not a URL string"); } Maybe result = node::loader::Resolve(env, specifier_std, url); if (result.IsNothing() || (result.FromJust().flags() & URL_FLAGS_FAILED)) { std::string msg = "Cannot find module " + specifier_std; return node::THROW_ERR_MISSING_MODULE(env, msg.c_str()); } MaybeLocal obj = result.FromJust().ToObject(env); if (!obj.IsEmpty()) args.GetReturnValue().Set(obj.ToLocalChecked()); } static MaybeLocal ImportModuleDynamically( Local context, Local referrer, Local specifier) { Isolate* iso = context->GetIsolate(); Environment* env = Environment::GetCurrent(context); CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here. v8::EscapableHandleScope handle_scope(iso); Local import_callback = env->host_import_module_dynamically_callback(); Local options = referrer->GetHostDefinedOptions(); if (options->Length() != HostDefinedOptions::kLength) { Local resolver = Promise::Resolver::New(context).ToLocalChecked(); resolver ->Reject(context, v8::Exception::TypeError(FIXED_ONE_BYTE_STRING( context->GetIsolate(), "Invalid host defined options"))) .ToChecked(); return handle_scope.Escape(resolver->GetPromise()); } Local object; int type = options->Get(iso, HostDefinedOptions::kType) .As() ->Int32Value(context) .ToChecked(); uint32_t id = options->Get(iso, HostDefinedOptions::kID) .As() ->Uint32Value(context) .ToChecked(); if (type == ScriptType::kScript) { contextify::ContextifyScript* wrap = env->id_to_script_map.find(id)->second; object = wrap->object(); } else if (type == ScriptType::kModule) { ModuleWrap* wrap = ModuleWrap::GetFromID(env, id); object = wrap->object(); } else { UNREACHABLE(); } Local import_args[] = { object, Local(specifier), }; Local result; if (import_callback->Call( context, v8::Undefined(iso), arraysize(import_args), import_args).ToLocal(&result)) { CHECK(result->IsPromise()); return handle_scope.Escape(result.As()); } return MaybeLocal(); } void ModuleWrap::SetImportModuleDynamicallyCallback( const FunctionCallbackInfo& args) { Isolate* iso = args.GetIsolate(); Environment* env = Environment::GetCurrent(args); HandleScope handle_scope(iso); CHECK_EQ(args.Length(), 1); CHECK(args[0]->IsFunction()); Local import_callback = args[0].As(); env->set_host_import_module_dynamically_callback(import_callback); iso->SetHostImportModuleDynamicallyCallback(ImportModuleDynamically); } void ModuleWrap::HostInitializeImportMetaObjectCallback( Local context, Local module, Local meta) { Environment* env = Environment::GetCurrent(context); CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here. ModuleWrap* module_wrap = GetFromModule(env, module); if (module_wrap == nullptr) { return; } Local wrap = module_wrap->object(); Local callback = env->host_initialize_import_meta_object_callback(); Local args[] = { wrap, meta }; callback->Call(context, Undefined(env->isolate()), arraysize(args), args) .ToLocalChecked(); } void ModuleWrap::SetInitializeImportMetaObjectCallback( const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); CHECK_EQ(args.Length(), 1); CHECK(args[0]->IsFunction()); Local import_meta_callback = args[0].As(); env->set_host_initialize_import_meta_object_callback(import_meta_callback); isolate->SetHostInitializeImportMetaObjectCallback( HostInitializeImportMetaObjectCallback); } void ModuleWrap::Initialize(Local target, Local unused, Local context, void* priv) { Environment* env = Environment::GetCurrent(context); Isolate* isolate = env->isolate(); Local tpl = env->NewFunctionTemplate(New); tpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "ModuleWrap")); tpl->InstanceTemplate()->SetInternalFieldCount(1); env->SetProtoMethod(tpl, "link", Link); env->SetProtoMethod(tpl, "instantiate", Instantiate); env->SetProtoMethod(tpl, "evaluate", Evaluate); env->SetProtoMethodNoSideEffect(tpl, "namespace", Namespace); env->SetProtoMethodNoSideEffect(tpl, "getStatus", GetStatus); env->SetProtoMethodNoSideEffect(tpl, "getError", GetError); env->SetProtoMethodNoSideEffect(tpl, "getStaticDependencySpecifiers", GetStaticDependencySpecifiers); target->Set(env->context(), FIXED_ONE_BYTE_STRING(isolate, "ModuleWrap"), tpl->GetFunction(context).ToLocalChecked()).FromJust(); env->SetMethod(target, "resolve", Resolve); env->SetMethod(target, "setImportModuleDynamicallyCallback", SetImportModuleDynamicallyCallback); env->SetMethod(target, "setInitializeImportMetaObjectCallback", SetInitializeImportMetaObjectCallback); #define V(name) \ target->Set(context, \ FIXED_ONE_BYTE_STRING(env->isolate(), #name), \ Integer::New(env->isolate(), Module::Status::name)) \ .FromJust() V(kUninstantiated); V(kInstantiating); V(kInstantiated); V(kEvaluating); V(kEvaluated); V(kErrored); #undef V } } // namespace loader } // namespace node NODE_MODULE_CONTEXT_AWARE_INTERNAL(module_wrap, node::loader::ModuleWrap::Initialize)