From 5a15f4bdfa060082757062dc5f39519d93e7b8fb Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Wed, 9 Jan 2019 00:00:33 +0800 Subject: src: move process object creation into node_process_object.cc Changes `SetupProcessObject` to `CreateProessObject` which creates the process object from scratch and return it to `Environment::Start` to be stored in the Environment object. PR-URL: https://github.com/nodejs/node/pull/25397 Reviewed-By: James M Snell Reviewed-By: Anna Henningsen --- node.gyp | 3 +- src/env.cc | 11 +- src/node.cc | 226 --------------------- src/node_internals.h | 7 +- src/node_process.cc | 463 -------------------------------------------- src/node_process_methods.cc | 463 ++++++++++++++++++++++++++++++++++++++++++++ src/node_process_object.cc | 258 ++++++++++++++++++++++++ 7 files changed, 728 insertions(+), 703 deletions(-) delete mode 100644 src/node_process.cc create mode 100644 src/node_process_methods.cc create mode 100644 src/node_process_object.cc diff --git a/node.gyp b/node.gyp index a0baf052be..36a6ea92dc 100644 --- a/node.gyp +++ b/node.gyp @@ -370,7 +370,8 @@ 'src/node_perf.cc', 'src/node_platform.cc', 'src/node_postmortem_metadata.cc', - 'src/node_process.cc', + 'src/node_process_methods.cc', + 'src/node_process_object.cc', 'src/node_serdes.cc', 'src/node_stat_watcher.cc', 'src/node_symbols.cc', diff --git a/src/env.cc b/src/env.cc index 068809e00a..19cc16fcfa 100644 --- a/src/env.cc +++ b/src/env.cc @@ -22,7 +22,6 @@ using v8::Context; using v8::EmbedderGraph; using v8::External; using v8::Function; -using v8::FunctionTemplate; using v8::HandleScope; using v8::Integer; using v8::Isolate; @@ -339,17 +338,9 @@ void Environment::Start(const std::vector& args, StartProfilerIdleNotifier(); } - auto process_template = FunctionTemplate::New(isolate()); - process_template->SetClassName(FIXED_ONE_BYTE_STRING(isolate(), "process")); - - auto process_object = process_template->GetFunction(context()) - .ToLocalChecked() - ->NewInstance(context()) - .ToLocalChecked(); + Local process_object = CreateProcessObject(this, args, exec_args); set_process_object(process_object); - SetupProcessObject(this, args, exec_args); - static uv_once_t init_once = UV_ONCE_INIT; uv_once(&init_once, InitThreadLocalOnce); uv_key_set(&thread_local_env, this); diff --git a/src/node.cc b/src/node.cc index e55fbef57a..59c13aba32 100644 --- a/src/node.cc +++ b/src/node.cc @@ -124,14 +124,12 @@ using v8::MaybeLocal; using v8::Message; using v8::MicrotasksPolicy; using v8::NewStringType; -using v8::None; using v8::Nothing; using v8::Object; using v8::ObjectTemplate; using v8::Script; using v8::ScriptOrigin; using v8::SealHandleScope; -using v8::SideEffectType; using v8::String; using v8::TracingController; using v8::Undefined; @@ -686,230 +684,6 @@ static void OnMessage(Local message, Local error) { } } -void SetupProcessObject(Environment* env, - const std::vector& args, - const std::vector& exec_args) { - Isolate* isolate = env->isolate(); - HandleScope scope(isolate); - Local context = env->context(); - - Local process = env->process_object(); - - auto title_string = FIXED_ONE_BYTE_STRING(env->isolate(), "title"); - CHECK(process->SetAccessor( - env->context(), - title_string, - ProcessTitleGetter, - env->is_main_thread() ? ProcessTitleSetter : nullptr, - env->as_external(), - DEFAULT, - None, - SideEffectType::kHasNoSideEffect).FromJust()); - - // process.version - READONLY_PROPERTY(process, - "version", - FIXED_ONE_BYTE_STRING(env->isolate(), NODE_VERSION)); - - // process.versions - Local versions = Object::New(env->isolate()); - READONLY_PROPERTY(process, "versions", versions); - -#define V(key) \ - if (!per_process::metadata.versions.key.empty()) { \ - READONLY_STRING_PROPERTY( \ - versions, #key, per_process::metadata.versions.key); \ - } - NODE_VERSIONS_KEYS(V) -#undef V - - // process.arch - READONLY_STRING_PROPERTY(process, "arch", per_process::metadata.arch); - - // process.platform - READONLY_STRING_PROPERTY(process, "platform", per_process::metadata.platform); - - // process.release - Local release = Object::New(env->isolate()); - READONLY_PROPERTY(process, "release", release); - READONLY_STRING_PROPERTY(release, "name", per_process::metadata.release.name); -#if NODE_VERSION_IS_LTS - READONLY_STRING_PROPERTY(release, "lts", per_process::metadata.release.lts); -#endif // NODE_VERSION_IS_LTS - -#ifdef NODE_HAS_RELEASE_URLS - READONLY_STRING_PROPERTY( - release, "sourceUrl", per_process::metadata.release.source_url); - READONLY_STRING_PROPERTY( - release, "headersUrl", per_process::metadata.release.headers_url); -#ifdef _WIN32 - READONLY_STRING_PROPERTY( - release, "libUrl", per_process::metadata.release.lib_url); -#endif // _WIN32 -#endif // NODE_HAS_RELEASE_URLS - - // process.argv - process->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "argv"), - ToV8Value(env->context(), args).ToLocalChecked()).FromJust(); - - // process.execArgv - process->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "execArgv"), - ToV8Value(env->context(), exec_args) - .ToLocalChecked()).FromJust(); - - // create process.env - process - ->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "env"), - CreateEnvVarProxy(context, isolate, env->as_external())) - .FromJust(); - - READONLY_PROPERTY(process, "pid", - Integer::New(env->isolate(), uv_os_getpid())); - - CHECK(process->SetAccessor(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "ppid"), - GetParentProcessId).FromJust()); - - // -e, --eval - // TODO(addaleax): Remove this. - if (env->options()->has_eval_string) { - READONLY_PROPERTY(process, - "_eval", - String::NewFromUtf8( - env->isolate(), - env->options()->eval_string.c_str(), - NewStringType::kNormal).ToLocalChecked()); - } - - // -p, --print - // TODO(addaleax): Remove this. - if (env->options()->print_eval) { - READONLY_PROPERTY(process, "_print_eval", True(env->isolate())); - } - - // -c, --check - // TODO(addaleax): Remove this. - if (env->options()->syntax_check_only) { - READONLY_PROPERTY(process, "_syntax_check_only", True(env->isolate())); - } - - // -i, --interactive - // TODO(addaleax): Remove this. - if (env->options()->force_repl) { - READONLY_PROPERTY(process, "_forceRepl", True(env->isolate())); - } - - // -r, --require - // TODO(addaleax): Remove this. - const std::vector& preload_modules = - env->options()->preload_modules; - if (!preload_modules.empty()) { - READONLY_PROPERTY(process, - "_preload_modules", - ToV8Value(env->context(), preload_modules) - .ToLocalChecked()); - } - - // --no-deprecation - if (env->options()->no_deprecation) { - READONLY_PROPERTY(process, "noDeprecation", True(env->isolate())); - } - - // --no-warnings - if (env->options()->no_warnings) { - READONLY_PROPERTY(process, "noProcessWarnings", True(env->isolate())); - } - - // --trace-warnings - if (env->options()->trace_warnings) { - READONLY_PROPERTY(process, "traceProcessWarnings", True(env->isolate())); - } - - // --throw-deprecation - if (env->options()->throw_deprecation) { - READONLY_PROPERTY(process, "throwDeprecation", True(env->isolate())); - } - -#ifdef NODE_NO_BROWSER_GLOBALS - // configure --no-browser-globals - READONLY_PROPERTY(process, "_noBrowserGlobals", True(env->isolate())); -#endif // NODE_NO_BROWSER_GLOBALS - - // --prof-process - // TODO(addaleax): Remove this. - if (env->options()->prof_process) { - READONLY_PROPERTY(process, "profProcess", True(env->isolate())); - } - - // --trace-deprecation - if (env->options()->trace_deprecation) { - READONLY_PROPERTY(process, "traceDeprecation", True(env->isolate())); - } - - // TODO(refack): move the following 4 to `node_config` - // --inspect-brk - if (env->options()->debug_options().wait_for_connect()) { - READONLY_DONT_ENUM_PROPERTY(process, - "_breakFirstLine", True(env->isolate())); - } - - if (env->options()->debug_options().break_node_first_line) { - READONLY_DONT_ENUM_PROPERTY(process, - "_breakNodeFirstLine", True(env->isolate())); - } - - // --inspect --debug-brk - if (env->options()->debug_options().deprecated_invocation()) { - READONLY_DONT_ENUM_PROPERTY(process, - "_deprecatedDebugBrk", True(env->isolate())); - } - - // --debug or, --debug-brk without --inspect - if (env->options()->debug_options().invalid_invocation()) { - READONLY_DONT_ENUM_PROPERTY(process, - "_invalidDebug", True(env->isolate())); - } - - // --security-revert flags -#define V(code, _, __) \ - do { \ - if (IsReverted(SECURITY_REVERT_ ## code)) { \ - READONLY_PROPERTY(process, "REVERT_" #code, True(env->isolate())); \ - } \ - } while (0); - SECURITY_REVERSIONS(V) -#undef V - - { - size_t exec_path_len = 2 * PATH_MAX; - std::vector exec_path(exec_path_len); - Local exec_path_value; - if (uv_exepath(exec_path.data(), &exec_path_len) == 0) { - exec_path_value = String::NewFromUtf8(env->isolate(), - exec_path.data(), - NewStringType::kInternalized, - exec_path_len).ToLocalChecked(); - } else { - exec_path_value = String::NewFromUtf8(env->isolate(), args[0].c_str(), - NewStringType::kInternalized).ToLocalChecked(); - } - process->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "execPath"), - exec_path_value).FromJust(); - } - - auto debug_port_string = FIXED_ONE_BYTE_STRING(env->isolate(), "debugPort"); - CHECK(process->SetAccessor(env->context(), - debug_port_string, - DebugPortGetter, - env->is_main_thread() ? DebugPortSetter : nullptr, - env->as_external()).FromJust()); -} - - void SignalExit(int signo) { uv_tty_reset_mode(); #ifdef __FreeBSD__ diff --git a/src/node_internals.h b/src/node_internals.h index 55e7d565fc..a192c519a3 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -185,9 +185,10 @@ v8::Maybe ProcessEmitDeprecationWarning(Environment* env, const char* warning, const char* deprecation_code); -void SetupProcessObject(Environment* env, - const std::vector& args, - const std::vector& exec_args); +v8::Local CreateProcessObject( + Environment* env, + const std::vector& args, + const std::vector& exec_args); enum Endianness { kLittleEndian, // _Not_ LITTLE_ENDIAN, clashes with endian.h. diff --git a/src/node_process.cc b/src/node_process.cc deleted file mode 100644 index 69f08a219b..0000000000 --- a/src/node_process.cc +++ /dev/null @@ -1,463 +0,0 @@ -#include "node.h" -#include "node_internals.h" -#include "node_errors.h" -#include "base_object.h" -#include "base_object-inl.h" -#include "env-inl.h" -#include "util-inl.h" -#include "uv.h" -#include "v8.h" - -#include - -#if HAVE_INSPECTOR -#include "inspector_io.h" -#endif - -#include // PATH_MAX -#include - -#if defined(_MSC_VER) -#include -#include -#define umask _umask -typedef int mode_t; -#else -#include -#include // getrlimit, setrlimit -#include // tcgetattr, tcsetattr -#endif - -namespace node { - -using v8::Array; -using v8::ArrayBuffer; -using v8::BigUint64Array; -using v8::Context; -using v8::Float64Array; -using v8::Function; -using v8::FunctionCallbackInfo; -using v8::HeapStatistics; -using v8::Integer; -using v8::Isolate; -using v8::Local; -using v8::Name; -using v8::NewStringType; -using v8::Object; -using v8::PropertyCallbackInfo; -using v8::String; -using v8::Uint32; -using v8::Uint32Array; -using v8::Value; - -// Microseconds in a second, as a float, used in CPUUsage() below -#define MICROS_PER_SEC 1e6 -// used in Hrtime() below -#define NANOS_PER_SEC 1000000000 - -#ifdef _WIN32 -/* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */ -#define CHDIR_BUFSIZE (MAX_PATH * 4) -#else -#define CHDIR_BUFSIZE (PATH_MAX) -#endif - -static void Abort(const FunctionCallbackInfo& args) { - Abort(); -} - -static void Chdir(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - CHECK(env->is_main_thread()); - - CHECK_EQ(args.Length(), 1); - CHECK(args[0]->IsString()); - Utf8Value path(env->isolate(), args[0]); - int err = uv_chdir(*path); - if (err) { - // Also include the original working directory, since that will usually - // be helpful information when debugging a `chdir()` failure. - char buf[CHDIR_BUFSIZE]; - size_t cwd_len = sizeof(buf); - uv_cwd(buf, &cwd_len); - return env->ThrowUVException(err, "chdir", nullptr, buf, *path); - } -} - -// CPUUsage use libuv's uv_getrusage() this-process resource usage accessor, -// to access ru_utime (user CPU time used) and ru_stime (system CPU time used), -// which are uv_timeval_t structs (long tv_sec, long tv_usec). -// Returns those values as Float64 microseconds in the elements of the array -// passed to the function. -static void CPUUsage(const FunctionCallbackInfo& args) { - uv_rusage_t rusage; - - // Call libuv to get the values we'll return. - int err = uv_getrusage(&rusage); - if (err) { - // On error, return the strerror version of the error code. - Local errmsg = OneByteString(args.GetIsolate(), uv_strerror(err)); - return args.GetReturnValue().Set(errmsg); - } - - // Get the double array pointer from the Float64Array argument. - CHECK(args[0]->IsFloat64Array()); - Local array = args[0].As(); - CHECK_EQ(array->Length(), 2); - Local ab = array->Buffer(); - double* fields = static_cast(ab->GetContents().Data()); - - // Set the Float64Array elements to be user / system values in microseconds. - fields[0] = MICROS_PER_SEC * rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec; - fields[1] = MICROS_PER_SEC * rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec; -} - -static void Cwd(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - char buf[CHDIR_BUFSIZE]; - size_t cwd_len = sizeof(buf); - int err = uv_cwd(buf, &cwd_len); - if (err) - return env->ThrowUVException(err, "uv_cwd"); - - Local cwd = String::NewFromUtf8(env->isolate(), - buf, - NewStringType::kNormal, - cwd_len).ToLocalChecked(); - args.GetReturnValue().Set(cwd); -} - - -// Hrtime exposes libuv's uv_hrtime() high-resolution timer. - -// This is the legacy version of hrtime before BigInt was introduced in -// JavaScript. -// The value returned by uv_hrtime() is a 64-bit int representing nanoseconds, -// so this function instead fills in an Uint32Array with 3 entries, -// to avoid any integer overflow possibility. -// The first two entries contain the second part of the value -// broken into the upper/lower 32 bits to be converted back in JS, -// because there is no Uint64Array in JS. -// The third entry contains the remaining nanosecond part of the value. -static void Hrtime(const FunctionCallbackInfo& args) { - uint64_t t = uv_hrtime(); - - Local ab = args[0].As()->Buffer(); - uint32_t* fields = static_cast(ab->GetContents().Data()); - - fields[0] = (t / NANOS_PER_SEC) >> 32; - fields[1] = (t / NANOS_PER_SEC) & 0xffffffff; - fields[2] = t % NANOS_PER_SEC; -} - -static void HrtimeBigInt(const FunctionCallbackInfo& args) { - Local ab = args[0].As()->Buffer(); - uint64_t* fields = static_cast(ab->GetContents().Data()); - fields[0] = uv_hrtime(); -} - -static void Kill(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - Local context = env->context(); - - if (args.Length() != 2) - return env->ThrowError("Bad argument."); - - int pid; - if (!args[0]->Int32Value(context).To(&pid)) return; - int sig; - if (!args[1]->Int32Value(context).To(&sig)) return; - int err = uv_kill(pid, sig); - args.GetReturnValue().Set(err); -} - -static void MemoryUsage(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - size_t rss; - int err = uv_resident_set_memory(&rss); - if (err) - return env->ThrowUVException(err, "uv_resident_set_memory"); - - Isolate* isolate = env->isolate(); - // V8 memory usage - HeapStatistics v8_heap_stats; - isolate->GetHeapStatistics(&v8_heap_stats); - - // Get the double array pointer from the Float64Array argument. - CHECK(args[0]->IsFloat64Array()); - Local array = args[0].As(); - CHECK_EQ(array->Length(), 4); - Local ab = array->Buffer(); - double* fields = static_cast(ab->GetContents().Data()); - - fields[0] = rss; - fields[1] = v8_heap_stats.total_heap_size(); - fields[2] = v8_heap_stats.used_heap_size(); - fields[3] = v8_heap_stats.external_memory(); -} - -// Most of the time, it's best to use `console.error` to write -// to the process.stderr stream. However, in some cases, such as -// when debugging the stream.Writable class or the process.nextTick -// function, it is useful to bypass JavaScript entirely. -void RawDebug(const FunctionCallbackInfo& args) { - CHECK(args.Length() == 1 && args[0]->IsString() && - "must be called with a single string"); - Utf8Value message(args.GetIsolate(), args[0]); - PrintErrorString("%s\n", *message); - fflush(stderr); -} - -static void StartProfilerIdleNotifier(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - env->StartProfilerIdleNotifier(); -} - -static void StopProfilerIdleNotifier(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - env->StopProfilerIdleNotifier(); -} - -static void Umask(const FunctionCallbackInfo& args) { - uint32_t old; - - CHECK_EQ(args.Length(), 1); - CHECK(args[0]->IsUndefined() || args[0]->IsUint32()); - - if (args[0]->IsUndefined()) { - old = umask(0); - umask(static_cast(old)); - } else { - int oct = args[0].As()->Value(); - old = umask(static_cast(oct)); - } - - args.GetReturnValue().Set(old); -} - -static void Uptime(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - double uptime; - - uv_update_time(env->event_loop()); - uptime = uv_now(env->event_loop()) - per_process::prog_start_time; - - args.GetReturnValue().Set(uptime / 1000); -} - -void ProcessTitleGetter(Local property, - const PropertyCallbackInfo& info) { - char buffer[512]; - uv_get_process_title(buffer, sizeof(buffer)); - info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), buffer, - NewStringType::kNormal).ToLocalChecked()); -} - -void ProcessTitleSetter(Local property, - Local value, - const PropertyCallbackInfo& info) { - node::Utf8Value title(info.GetIsolate(), value); - TRACE_EVENT_METADATA1("__metadata", "process_name", "name", - TRACE_STR_COPY(*title)); - uv_set_process_title(*title); -} - -void GetParentProcessId(Local property, - const PropertyCallbackInfo& info) { - info.GetReturnValue().Set(uv_os_getppid()); -} - -static void GetActiveRequests(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - std::vector> request_v; - for (auto w : *env->req_wrap_queue()) { - if (w->persistent().IsEmpty()) - continue; - request_v.push_back(w->GetOwner()); - } - - args.GetReturnValue().Set( - Array::New(env->isolate(), request_v.data(), request_v.size())); -} - -// Non-static, friend of HandleWrap. Could have been a HandleWrap method but -// implemented here for consistency with GetActiveRequests(). -void GetActiveHandles(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - std::vector> handle_v; - for (auto w : *env->handle_wrap_queue()) { - if (!HandleWrap::HasRef(w)) - continue; - handle_v.push_back(w->GetOwner()); - } - args.GetReturnValue().Set( - Array::New(env->isolate(), handle_v.data(), handle_v.size())); -} - -void DebugPortGetter(Local property, - const PropertyCallbackInfo& info) { - Environment* env = Environment::GetCurrent(info); - int port = env->inspector_host_port()->port(); - info.GetReturnValue().Set(port); -} - - -void DebugPortSetter(Local property, - Local value, - const PropertyCallbackInfo& info) { - Environment* env = Environment::GetCurrent(info); - int32_t port = value->Int32Value(env->context()).FromMaybe(0); - env->inspector_host_port()->set_port(static_cast(port)); -} - -#ifdef __POSIX__ -static void DebugProcess(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - if (args.Length() != 1) { - return env->ThrowError("Invalid number of arguments."); - } - - CHECK(args[0]->IsNumber()); - pid_t pid = args[0].As()->Value(); - int r = kill(pid, SIGUSR1); - - if (r != 0) { - return env->ThrowErrnoException(errno, "kill"); - } -} -#endif // __POSIX__ - -#ifdef _WIN32 -static int GetDebugSignalHandlerMappingName(DWORD pid, - wchar_t* buf, - size_t buf_len) { - return _snwprintf(buf, buf_len, L"node-debug-handler-%u", pid); -} - -static void DebugProcess(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - Isolate* isolate = args.GetIsolate(); - - if (args.Length() != 1) { - env->ThrowError("Invalid number of arguments."); - return; - } - - HANDLE process = nullptr; - HANDLE thread = nullptr; - HANDLE mapping = nullptr; - wchar_t mapping_name[32]; - LPTHREAD_START_ROUTINE* handler = nullptr; - DWORD pid = 0; - - OnScopeLeave cleanup([&]() { - if (process != nullptr) CloseHandle(process); - if (thread != nullptr) CloseHandle(thread); - if (handler != nullptr) UnmapViewOfFile(handler); - if (mapping != nullptr) CloseHandle(mapping); - }); - - CHECK(args[0]->IsNumber()); - pid = args[0].As()->Value(); - - process = - OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | - PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, - FALSE, - pid); - if (process == nullptr) { - isolate->ThrowException( - WinapiErrnoException(isolate, GetLastError(), "OpenProcess")); - return; - } - - if (GetDebugSignalHandlerMappingName( - pid, mapping_name, arraysize(mapping_name)) < 0) { - env->ThrowErrnoException(errno, "sprintf"); - return; - } - - mapping = OpenFileMappingW(FILE_MAP_READ, FALSE, mapping_name); - if (mapping == nullptr) { - isolate->ThrowException( - WinapiErrnoException(isolate, GetLastError(), "OpenFileMappingW")); - return; - } - - handler = reinterpret_cast( - MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, sizeof *handler)); - if (handler == nullptr || *handler == nullptr) { - isolate->ThrowException( - WinapiErrnoException(isolate, GetLastError(), "MapViewOfFile")); - return; - } - - thread = - CreateRemoteThread(process, nullptr, 0, *handler, nullptr, 0, nullptr); - if (thread == nullptr) { - isolate->ThrowException( - WinapiErrnoException(isolate, GetLastError(), "CreateRemoteThread")); - return; - } - - // Wait for the thread to terminate - if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0) { - isolate->ThrowException( - WinapiErrnoException(isolate, GetLastError(), "WaitForSingleObject")); - return; - } -} -#endif // _WIN32 - -static void DebugEnd(const FunctionCallbackInfo& args) { -#if HAVE_INSPECTOR - Environment* env = Environment::GetCurrent(args); - if (env->inspector_agent()->IsListening()) { - env->inspector_agent()->Stop(); - } -#endif -} - -static void InitializeProcessMethods(Local target, - Local unused, - Local context, - void* priv) { - Environment* env = Environment::GetCurrent(context); - - // define various internal methods - if (env->is_main_thread()) { - env->SetMethod(target, "_debugProcess", DebugProcess); - env->SetMethod(target, "_debugEnd", DebugEnd); - env->SetMethod( - target, "_startProfilerIdleNotifier", StartProfilerIdleNotifier); - env->SetMethod( - target, "_stopProfilerIdleNotifier", StopProfilerIdleNotifier); - env->SetMethod(target, "abort", Abort); - env->SetMethod(target, "chdir", Chdir); - env->SetMethod(target, "umask", Umask); - } - - env->SetMethod(target, "_rawDebug", RawDebug); - env->SetMethod(target, "memoryUsage", MemoryUsage); - env->SetMethod(target, "cpuUsage", CPUUsage); - env->SetMethod(target, "hrtime", Hrtime); - env->SetMethod(target, "hrtimeBigInt", HrtimeBigInt); - - env->SetMethod(target, "_getActiveRequests", GetActiveRequests); - env->SetMethod(target, "_getActiveHandles", GetActiveHandles); - env->SetMethod(target, "_kill", Kill); - - env->SetMethodNoSideEffect(target, "cwd", Cwd); - env->SetMethod(target, "dlopen", binding::DLOpen); - env->SetMethod(target, "reallyExit", Exit); - env->SetMethodNoSideEffect(target, "uptime", Uptime); -} - -} // namespace node - -NODE_MODULE_CONTEXT_AWARE_INTERNAL(process_methods, - node::InitializeProcessMethods) diff --git a/src/node_process_methods.cc b/src/node_process_methods.cc new file mode 100644 index 0000000000..69f08a219b --- /dev/null +++ b/src/node_process_methods.cc @@ -0,0 +1,463 @@ +#include "node.h" +#include "node_internals.h" +#include "node_errors.h" +#include "base_object.h" +#include "base_object-inl.h" +#include "env-inl.h" +#include "util-inl.h" +#include "uv.h" +#include "v8.h" + +#include + +#if HAVE_INSPECTOR +#include "inspector_io.h" +#endif + +#include // PATH_MAX +#include + +#if defined(_MSC_VER) +#include +#include +#define umask _umask +typedef int mode_t; +#else +#include +#include // getrlimit, setrlimit +#include // tcgetattr, tcsetattr +#endif + +namespace node { + +using v8::Array; +using v8::ArrayBuffer; +using v8::BigUint64Array; +using v8::Context; +using v8::Float64Array; +using v8::Function; +using v8::FunctionCallbackInfo; +using v8::HeapStatistics; +using v8::Integer; +using v8::Isolate; +using v8::Local; +using v8::Name; +using v8::NewStringType; +using v8::Object; +using v8::PropertyCallbackInfo; +using v8::String; +using v8::Uint32; +using v8::Uint32Array; +using v8::Value; + +// Microseconds in a second, as a float, used in CPUUsage() below +#define MICROS_PER_SEC 1e6 +// used in Hrtime() below +#define NANOS_PER_SEC 1000000000 + +#ifdef _WIN32 +/* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */ +#define CHDIR_BUFSIZE (MAX_PATH * 4) +#else +#define CHDIR_BUFSIZE (PATH_MAX) +#endif + +static void Abort(const FunctionCallbackInfo& args) { + Abort(); +} + +static void Chdir(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(env->is_main_thread()); + + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsString()); + Utf8Value path(env->isolate(), args[0]); + int err = uv_chdir(*path); + if (err) { + // Also include the original working directory, since that will usually + // be helpful information when debugging a `chdir()` failure. + char buf[CHDIR_BUFSIZE]; + size_t cwd_len = sizeof(buf); + uv_cwd(buf, &cwd_len); + return env->ThrowUVException(err, "chdir", nullptr, buf, *path); + } +} + +// CPUUsage use libuv's uv_getrusage() this-process resource usage accessor, +// to access ru_utime (user CPU time used) and ru_stime (system CPU time used), +// which are uv_timeval_t structs (long tv_sec, long tv_usec). +// Returns those values as Float64 microseconds in the elements of the array +// passed to the function. +static void CPUUsage(const FunctionCallbackInfo& args) { + uv_rusage_t rusage; + + // Call libuv to get the values we'll return. + int err = uv_getrusage(&rusage); + if (err) { + // On error, return the strerror version of the error code. + Local errmsg = OneByteString(args.GetIsolate(), uv_strerror(err)); + return args.GetReturnValue().Set(errmsg); + } + + // Get the double array pointer from the Float64Array argument. + CHECK(args[0]->IsFloat64Array()); + Local array = args[0].As(); + CHECK_EQ(array->Length(), 2); + Local ab = array->Buffer(); + double* fields = static_cast(ab->GetContents().Data()); + + // Set the Float64Array elements to be user / system values in microseconds. + fields[0] = MICROS_PER_SEC * rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec; + fields[1] = MICROS_PER_SEC * rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec; +} + +static void Cwd(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + char buf[CHDIR_BUFSIZE]; + size_t cwd_len = sizeof(buf); + int err = uv_cwd(buf, &cwd_len); + if (err) + return env->ThrowUVException(err, "uv_cwd"); + + Local cwd = String::NewFromUtf8(env->isolate(), + buf, + NewStringType::kNormal, + cwd_len).ToLocalChecked(); + args.GetReturnValue().Set(cwd); +} + + +// Hrtime exposes libuv's uv_hrtime() high-resolution timer. + +// This is the legacy version of hrtime before BigInt was introduced in +// JavaScript. +// The value returned by uv_hrtime() is a 64-bit int representing nanoseconds, +// so this function instead fills in an Uint32Array with 3 entries, +// to avoid any integer overflow possibility. +// The first two entries contain the second part of the value +// broken into the upper/lower 32 bits to be converted back in JS, +// because there is no Uint64Array in JS. +// The third entry contains the remaining nanosecond part of the value. +static void Hrtime(const FunctionCallbackInfo& args) { + uint64_t t = uv_hrtime(); + + Local ab = args[0].As()->Buffer(); + uint32_t* fields = static_cast(ab->GetContents().Data()); + + fields[0] = (t / NANOS_PER_SEC) >> 32; + fields[1] = (t / NANOS_PER_SEC) & 0xffffffff; + fields[2] = t % NANOS_PER_SEC; +} + +static void HrtimeBigInt(const FunctionCallbackInfo& args) { + Local ab = args[0].As()->Buffer(); + uint64_t* fields = static_cast(ab->GetContents().Data()); + fields[0] = uv_hrtime(); +} + +static void Kill(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + Local context = env->context(); + + if (args.Length() != 2) + return env->ThrowError("Bad argument."); + + int pid; + if (!args[0]->Int32Value(context).To(&pid)) return; + int sig; + if (!args[1]->Int32Value(context).To(&sig)) return; + int err = uv_kill(pid, sig); + args.GetReturnValue().Set(err); +} + +static void MemoryUsage(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + size_t rss; + int err = uv_resident_set_memory(&rss); + if (err) + return env->ThrowUVException(err, "uv_resident_set_memory"); + + Isolate* isolate = env->isolate(); + // V8 memory usage + HeapStatistics v8_heap_stats; + isolate->GetHeapStatistics(&v8_heap_stats); + + // Get the double array pointer from the Float64Array argument. + CHECK(args[0]->IsFloat64Array()); + Local array = args[0].As(); + CHECK_EQ(array->Length(), 4); + Local ab = array->Buffer(); + double* fields = static_cast(ab->GetContents().Data()); + + fields[0] = rss; + fields[1] = v8_heap_stats.total_heap_size(); + fields[2] = v8_heap_stats.used_heap_size(); + fields[3] = v8_heap_stats.external_memory(); +} + +// Most of the time, it's best to use `console.error` to write +// to the process.stderr stream. However, in some cases, such as +// when debugging the stream.Writable class or the process.nextTick +// function, it is useful to bypass JavaScript entirely. +void RawDebug(const FunctionCallbackInfo& args) { + CHECK(args.Length() == 1 && args[0]->IsString() && + "must be called with a single string"); + Utf8Value message(args.GetIsolate(), args[0]); + PrintErrorString("%s\n", *message); + fflush(stderr); +} + +static void StartProfilerIdleNotifier(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + env->StartProfilerIdleNotifier(); +} + +static void StopProfilerIdleNotifier(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + env->StopProfilerIdleNotifier(); +} + +static void Umask(const FunctionCallbackInfo& args) { + uint32_t old; + + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsUndefined() || args[0]->IsUint32()); + + if (args[0]->IsUndefined()) { + old = umask(0); + umask(static_cast(old)); + } else { + int oct = args[0].As()->Value(); + old = umask(static_cast(oct)); + } + + args.GetReturnValue().Set(old); +} + +static void Uptime(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + double uptime; + + uv_update_time(env->event_loop()); + uptime = uv_now(env->event_loop()) - per_process::prog_start_time; + + args.GetReturnValue().Set(uptime / 1000); +} + +void ProcessTitleGetter(Local property, + const PropertyCallbackInfo& info) { + char buffer[512]; + uv_get_process_title(buffer, sizeof(buffer)); + info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), buffer, + NewStringType::kNormal).ToLocalChecked()); +} + +void ProcessTitleSetter(Local property, + Local value, + const PropertyCallbackInfo& info) { + node::Utf8Value title(info.GetIsolate(), value); + TRACE_EVENT_METADATA1("__metadata", "process_name", "name", + TRACE_STR_COPY(*title)); + uv_set_process_title(*title); +} + +void GetParentProcessId(Local property, + const PropertyCallbackInfo& info) { + info.GetReturnValue().Set(uv_os_getppid()); +} + +static void GetActiveRequests(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + std::vector> request_v; + for (auto w : *env->req_wrap_queue()) { + if (w->persistent().IsEmpty()) + continue; + request_v.push_back(w->GetOwner()); + } + + args.GetReturnValue().Set( + Array::New(env->isolate(), request_v.data(), request_v.size())); +} + +// Non-static, friend of HandleWrap. Could have been a HandleWrap method but +// implemented here for consistency with GetActiveRequests(). +void GetActiveHandles(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + std::vector> handle_v; + for (auto w : *env->handle_wrap_queue()) { + if (!HandleWrap::HasRef(w)) + continue; + handle_v.push_back(w->GetOwner()); + } + args.GetReturnValue().Set( + Array::New(env->isolate(), handle_v.data(), handle_v.size())); +} + +void DebugPortGetter(Local property, + const PropertyCallbackInfo& info) { + Environment* env = Environment::GetCurrent(info); + int port = env->inspector_host_port()->port(); + info.GetReturnValue().Set(port); +} + + +void DebugPortSetter(Local property, + Local value, + const PropertyCallbackInfo& info) { + Environment* env = Environment::GetCurrent(info); + int32_t port = value->Int32Value(env->context()).FromMaybe(0); + env->inspector_host_port()->set_port(static_cast(port)); +} + +#ifdef __POSIX__ +static void DebugProcess(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + if (args.Length() != 1) { + return env->ThrowError("Invalid number of arguments."); + } + + CHECK(args[0]->IsNumber()); + pid_t pid = args[0].As()->Value(); + int r = kill(pid, SIGUSR1); + + if (r != 0) { + return env->ThrowErrnoException(errno, "kill"); + } +} +#endif // __POSIX__ + +#ifdef _WIN32 +static int GetDebugSignalHandlerMappingName(DWORD pid, + wchar_t* buf, + size_t buf_len) { + return _snwprintf(buf, buf_len, L"node-debug-handler-%u", pid); +} + +static void DebugProcess(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + Isolate* isolate = args.GetIsolate(); + + if (args.Length() != 1) { + env->ThrowError("Invalid number of arguments."); + return; + } + + HANDLE process = nullptr; + HANDLE thread = nullptr; + HANDLE mapping = nullptr; + wchar_t mapping_name[32]; + LPTHREAD_START_ROUTINE* handler = nullptr; + DWORD pid = 0; + + OnScopeLeave cleanup([&]() { + if (process != nullptr) CloseHandle(process); + if (thread != nullptr) CloseHandle(thread); + if (handler != nullptr) UnmapViewOfFile(handler); + if (mapping != nullptr) CloseHandle(mapping); + }); + + CHECK(args[0]->IsNumber()); + pid = args[0].As()->Value(); + + process = + OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | + PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, + FALSE, + pid); + if (process == nullptr) { + isolate->ThrowException( + WinapiErrnoException(isolate, GetLastError(), "OpenProcess")); + return; + } + + if (GetDebugSignalHandlerMappingName( + pid, mapping_name, arraysize(mapping_name)) < 0) { + env->ThrowErrnoException(errno, "sprintf"); + return; + } + + mapping = OpenFileMappingW(FILE_MAP_READ, FALSE, mapping_name); + if (mapping == nullptr) { + isolate->ThrowException( + WinapiErrnoException(isolate, GetLastError(), "OpenFileMappingW")); + return; + } + + handler = reinterpret_cast( + MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, sizeof *handler)); + if (handler == nullptr || *handler == nullptr) { + isolate->ThrowException( + WinapiErrnoException(isolate, GetLastError(), "MapViewOfFile")); + return; + } + + thread = + CreateRemoteThread(process, nullptr, 0, *handler, nullptr, 0, nullptr); + if (thread == nullptr) { + isolate->ThrowException( + WinapiErrnoException(isolate, GetLastError(), "CreateRemoteThread")); + return; + } + + // Wait for the thread to terminate + if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0) { + isolate->ThrowException( + WinapiErrnoException(isolate, GetLastError(), "WaitForSingleObject")); + return; + } +} +#endif // _WIN32 + +static void DebugEnd(const FunctionCallbackInfo& args) { +#if HAVE_INSPECTOR + Environment* env = Environment::GetCurrent(args); + if (env->inspector_agent()->IsListening()) { + env->inspector_agent()->Stop(); + } +#endif +} + +static void InitializeProcessMethods(Local target, + Local unused, + Local context, + void* priv) { + Environment* env = Environment::GetCurrent(context); + + // define various internal methods + if (env->is_main_thread()) { + env->SetMethod(target, "_debugProcess", DebugProcess); + env->SetMethod(target, "_debugEnd", DebugEnd); + env->SetMethod( + target, "_startProfilerIdleNotifier", StartProfilerIdleNotifier); + env->SetMethod( + target, "_stopProfilerIdleNotifier", StopProfilerIdleNotifier); + env->SetMethod(target, "abort", Abort); + env->SetMethod(target, "chdir", Chdir); + env->SetMethod(target, "umask", Umask); + } + + env->SetMethod(target, "_rawDebug", RawDebug); + env->SetMethod(target, "memoryUsage", MemoryUsage); + env->SetMethod(target, "cpuUsage", CPUUsage); + env->SetMethod(target, "hrtime", Hrtime); + env->SetMethod(target, "hrtimeBigInt", HrtimeBigInt); + + env->SetMethod(target, "_getActiveRequests", GetActiveRequests); + env->SetMethod(target, "_getActiveHandles", GetActiveHandles); + env->SetMethod(target, "_kill", Kill); + + env->SetMethodNoSideEffect(target, "cwd", Cwd); + env->SetMethod(target, "dlopen", binding::DLOpen); + env->SetMethod(target, "reallyExit", Exit); + env->SetMethodNoSideEffect(target, "uptime", Uptime); +} + +} // namespace node + +NODE_MODULE_CONTEXT_AWARE_INTERNAL(process_methods, + node::InitializeProcessMethods) diff --git a/src/node_process_object.cc b/src/node_process_object.cc new file mode 100644 index 0000000000..07f28653de --- /dev/null +++ b/src/node_process_object.cc @@ -0,0 +1,258 @@ +#include // PATH_MAX + +#include "env-inl.h" +#include "node_internals.h" +#include "node_options-inl.h" +#include "node_metadata.h" +#include "node_revert.h" +#include "util-inl.h" + +namespace node { +using v8::Context; +using v8::DEFAULT; +using v8::EscapableHandleScope; +using v8::Function; +using v8::FunctionTemplate; +using v8::HandleScope; +using v8::Integer; +using v8::Isolate; +using v8::Just; +using v8::Local; +using v8::NewStringType; +using v8::None; +using v8::Object; +using v8::SideEffectType; +using v8::String; +using v8::Value; + +Local CreateProcessObject(Environment* env, + const std::vector& args, + const std::vector& exec_args) { + Isolate* isolate = env->isolate(); + EscapableHandleScope scope(isolate); + Local context = env->context(); + + Local process_template = FunctionTemplate::New(isolate); + process_template->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "process")); + Local process = process_template->GetFunction(context) + .ToLocalChecked() + ->NewInstance(context) + .ToLocalChecked(); + + auto title_string = FIXED_ONE_BYTE_STRING(env->isolate(), "title"); + CHECK(process->SetAccessor( + env->context(), + title_string, + ProcessTitleGetter, + env->is_main_thread() ? ProcessTitleSetter : nullptr, + env->as_external(), + DEFAULT, + None, + SideEffectType::kHasNoSideEffect).FromJust()); + + // process.version + READONLY_PROPERTY(process, + "version", + FIXED_ONE_BYTE_STRING(env->isolate(), NODE_VERSION)); + + // process.versions + Local versions = Object::New(env->isolate()); + READONLY_PROPERTY(process, "versions", versions); + +#define V(key) \ + if (!per_process::metadata.versions.key.empty()) { \ + READONLY_STRING_PROPERTY( \ + versions, #key, per_process::metadata.versions.key); \ + } + NODE_VERSIONS_KEYS(V) +#undef V + + // process.arch + READONLY_STRING_PROPERTY(process, "arch", per_process::metadata.arch); + + // process.platform + READONLY_STRING_PROPERTY(process, "platform", per_process::metadata.platform); + + // process.release + Local release = Object::New(env->isolate()); + READONLY_PROPERTY(process, "release", release); + READONLY_STRING_PROPERTY(release, "name", per_process::metadata.release.name); +#if NODE_VERSION_IS_LTS + READONLY_STRING_PROPERTY(release, "lts", per_process::metadata.release.lts); +#endif // NODE_VERSION_IS_LTS + +#ifdef NODE_HAS_RELEASE_URLS + READONLY_STRING_PROPERTY( + release, "sourceUrl", per_process::metadata.release.source_url); + READONLY_STRING_PROPERTY( + release, "headersUrl", per_process::metadata.release.headers_url); +#ifdef _WIN32 + READONLY_STRING_PROPERTY( + release, "libUrl", per_process::metadata.release.lib_url); +#endif // _WIN32 +#endif // NODE_HAS_RELEASE_URLS + + // process.argv + process->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "argv"), + ToV8Value(env->context(), args).ToLocalChecked()).FromJust(); + + // process.execArgv + process->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "execArgv"), + ToV8Value(env->context(), exec_args) + .ToLocalChecked()).FromJust(); + + // create process.env + process + ->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "env"), + CreateEnvVarProxy(context, isolate, env->as_external())) + .FromJust(); + + READONLY_PROPERTY(process, "pid", + Integer::New(env->isolate(), uv_os_getpid())); + + CHECK(process->SetAccessor(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "ppid"), + GetParentProcessId).FromJust()); + + // -e, --eval + // TODO(addaleax): Remove this. + if (env->options()->has_eval_string) { + READONLY_PROPERTY(process, + "_eval", + String::NewFromUtf8( + env->isolate(), + env->options()->eval_string.c_str(), + NewStringType::kNormal).ToLocalChecked()); + } + + // -p, --print + // TODO(addaleax): Remove this. + if (env->options()->print_eval) { + READONLY_PROPERTY(process, "_print_eval", True(env->isolate())); + } + + // -c, --check + // TODO(addaleax): Remove this. + if (env->options()->syntax_check_only) { + READONLY_PROPERTY(process, "_syntax_check_only", True(env->isolate())); + } + + // -i, --interactive + // TODO(addaleax): Remove this. + if (env->options()->force_repl) { + READONLY_PROPERTY(process, "_forceRepl", True(env->isolate())); + } + + // -r, --require + // TODO(addaleax): Remove this. + const std::vector& preload_modules = + env->options()->preload_modules; + if (!preload_modules.empty()) { + READONLY_PROPERTY(process, + "_preload_modules", + ToV8Value(env->context(), preload_modules) + .ToLocalChecked()); + } + + // --no-deprecation + if (env->options()->no_deprecation) { + READONLY_PROPERTY(process, "noDeprecation", True(env->isolate())); + } + + // --no-warnings + if (env->options()->no_warnings) { + READONLY_PROPERTY(process, "noProcessWarnings", True(env->isolate())); + } + + // --trace-warnings + if (env->options()->trace_warnings) { + READONLY_PROPERTY(process, "traceProcessWarnings", True(env->isolate())); + } + + // --throw-deprecation + if (env->options()->throw_deprecation) { + READONLY_PROPERTY(process, "throwDeprecation", True(env->isolate())); + } + +#ifdef NODE_NO_BROWSER_GLOBALS + // configure --no-browser-globals + READONLY_PROPERTY(process, "_noBrowserGlobals", True(env->isolate())); +#endif // NODE_NO_BROWSER_GLOBALS + + // --prof-process + // TODO(addaleax): Remove this. + if (env->options()->prof_process) { + READONLY_PROPERTY(process, "profProcess", True(env->isolate())); + } + + // --trace-deprecation + if (env->options()->trace_deprecation) { + READONLY_PROPERTY(process, "traceDeprecation", True(env->isolate())); + } + + // TODO(refack): move the following 4 to `node_config` + // --inspect-brk + if (env->options()->debug_options().wait_for_connect()) { + READONLY_DONT_ENUM_PROPERTY(process, + "_breakFirstLine", True(env->isolate())); + } + + if (env->options()->debug_options().break_node_first_line) { + READONLY_DONT_ENUM_PROPERTY(process, + "_breakNodeFirstLine", True(env->isolate())); + } + + // --inspect --debug-brk + if (env->options()->debug_options().deprecated_invocation()) { + READONLY_DONT_ENUM_PROPERTY(process, + "_deprecatedDebugBrk", True(env->isolate())); + } + + // --debug or, --debug-brk without --inspect + if (env->options()->debug_options().invalid_invocation()) { + READONLY_DONT_ENUM_PROPERTY(process, + "_invalidDebug", True(env->isolate())); + } + + // --security-revert flags +#define V(code, _, __) \ + do { \ + if (IsReverted(SECURITY_REVERT_ ## code)) { \ + READONLY_PROPERTY(process, "REVERT_" #code, True(env->isolate())); \ + } \ + } while (0); + SECURITY_REVERSIONS(V) +#undef V + + { + size_t exec_path_len = 2 * PATH_MAX; + std::vector exec_path(exec_path_len); + Local exec_path_value; + if (uv_exepath(exec_path.data(), &exec_path_len) == 0) { + exec_path_value = String::NewFromUtf8(env->isolate(), + exec_path.data(), + NewStringType::kInternalized, + exec_path_len).ToLocalChecked(); + } else { + exec_path_value = String::NewFromUtf8(env->isolate(), args[0].c_str(), + NewStringType::kInternalized).ToLocalChecked(); + } + process->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "execPath"), + exec_path_value).FromJust(); + } + + auto debug_port_string = FIXED_ONE_BYTE_STRING(env->isolate(), "debugPort"); + CHECK(process->SetAccessor(env->context(), + debug_port_string, + DebugPortGetter, + env->is_main_thread() ? DebugPortSetter : nullptr, + env->as_external()).FromJust()); + + return scope.Escape(process); +} + +} // namespace node -- cgit v1.2.3