From 427839e8e850bee4aec9372ec51c161e9e1bff6b Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Wed, 19 Dec 2018 09:53:12 +0800 Subject: [PATCH 1/8] src: move more process methods initialization in bootstrap/node.js Instead of: - Writing methods onto the process directly in C++ during `SetupProcessObject()` and overwrite with argument checks later - Or, wrapping and writing them in `internal/process/*.js` Do: - Move the C++ implementations in node_process.cc and mark them static wherever possible - Expose the C++ methods through a new `internalBinding('process_methods')` - Wrap the methods in `internal/process/*.js` in a side-effect-free manner and return them back to `internal/bootstrap/node.js` - Centralize the write to the process object based on conditions in `bootstrap/node.js` So it's easier to see what methods are attached to the process object during bootstrap under what condition and in what order. The eventual goal is to figure out the dependency of process methods and the write/read access to the process object during bootstrap, group these access properly and remove the process properties that should not be exposed to users this way. Also correct the NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE milestone which should be marked before code execution. Refs: https://github.com/nodejs/node/issues/24961 PR-URL: https://github.com/nodejs/node/pull/25127 Reviewed-By: Anna Henningsen Reviewed-By: James M Snell --- lib/internal/bootstrap/node.js | 100 +++++++++--- lib/internal/process/main_thread_only.js | 24 +-- lib/internal/process/per_thread.js | 141 +++++++---------- src/bootstrapper.cc | 14 -- src/node.cc | 155 +------------------ src/node_binding.cc | 1 + src/node_internals.h | 15 +- src/node_process.cc | 187 +++++++++++++++++++++-- test/parallel/test-bootstrap-modules.js | 2 +- 9 files changed, 322 insertions(+), 317 deletions(-) diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index 4604fadb8bf3d0..6430436eb0609e 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -19,11 +19,7 @@ const { _setupNextTick, - _setupPromises, _chdir, _cpuUsage, - _hrtime, _hrtimeBigInt, - _memoryUsage, _rawDebug, - _umask, - _shouldAbortOnUncaughtToggle + _setupPromises } = bootstrappers; const { internalBinding, NativeModule } = loaderExports; @@ -56,15 +52,57 @@ function startup() { ); } - perThreadSetup.setupAssert(); - perThreadSetup.setupConfig(); + // process.config is serialized config.gypi + process.config = JSON.parse(internalBinding('native_module').config); + const rawMethods = internalBinding('process_methods'); + // Set up methods and events on the process object for the main thread if (isMainThread) { + // This depends on process being an event emitter mainThreadSetup.setupSignalHandlers(internalBinding); + + process.abort = rawMethods.abort; + const wrapped = mainThreadSetup.wrapProcessMethods(rawMethods); + process.umask = wrapped.umask; + process.chdir = wrapped.chdir; + + // TODO(joyeecheung): deprecate and remove these underscore methods + process._debugProcess = rawMethods._debugProcess; + process._debugEnd = rawMethods._debugEnd; + process._startProfilerIdleNotifier = + rawMethods._startProfilerIdleNotifier; + process._stopProfilerIdleNotifier = rawMethods._stopProfilerIdleNotifier; } - perThreadSetup.setupUncaughtExceptionCapture(exceptionHandlerState, - _shouldAbortOnUncaughtToggle); + // Set up methods on the process object for all threads + { + process.cwd = rawMethods.cwd; + process.dlopen = rawMethods.dlopen; + process.uptime = rawMethods.uptime; + + // TODO(joyeecheung): either remove them or make them public + process._getActiveRequests = rawMethods._getActiveRequests; + process._getActiveHandles = rawMethods._getActiveHandles; + + // TODO(joyeecheung): remove these + process.reallyExit = rawMethods.reallyExit; + process._kill = rawMethods._kill; + + const wrapped = perThreadSetup.wrapProcessMethods( + rawMethods, exceptionHandlerState + ); + process._rawDebug = wrapped._rawDebug; + process.hrtime = wrapped.hrtime; + process.hrtime.bigint = wrapped.hrtimeBigInt; + process.cpuUsage = wrapped.cpuUsage; + process.memoryUsage = wrapped.memoryUsage; + process.kill = wrapped.kill; + process.exit = wrapped.exit; + process.setUncaughtExceptionCaptureCallback = + wrapped.setUncaughtExceptionCaptureCallback; + process.hasUncaughtExceptionCaptureCallback = + wrapped.hasUncaughtExceptionCaptureCallback; + } NativeModule.require('internal/process/warning').setup(); NativeModule.require('internal/process/next_tick').setup(_setupNextTick, @@ -90,22 +128,10 @@ function startup() { if (isMainThread) { mainThreadSetup.setupStdio(); - mainThreadSetup.setupProcessMethods(_chdir, _umask); } else { workerThreadSetup.setupStdio(); } - const perf = internalBinding('performance'); - const { - NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE, - } = perf.constants; - - perThreadSetup.setupRawDebug(_rawDebug); - perThreadSetup.setupHrtime(_hrtime, _hrtimeBigInt); - perThreadSetup.setupCpuUsage(_cpuUsage); - perThreadSetup.setupMemoryUsage(_memoryUsage); - perThreadSetup.setupKillAndExit(); - if (global.__coverage__) NativeModule.require('internal/process/write-coverage').setup(); @@ -237,9 +263,37 @@ function startup() { } } - perf.markMilestone(NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE); + // process.allowedNodeEnvironmentFlags + Object.defineProperty(process, 'allowedNodeEnvironmentFlags', { + get() { + const flags = perThreadSetup.buildAllowedFlags(); + process.allowedNodeEnvironmentFlags = flags; + return process.allowedNodeEnvironmentFlags; + }, + // If the user tries to set this to another value, override + // this completely to that value. + set(value) { + Object.defineProperty(this, 'allowedNodeEnvironmentFlags', { + value, + configurable: true, + enumerable: true, + writable: true + }); + }, + enumerable: true, + configurable: true + }); + // process.assert + process.assert = deprecate( + perThreadSetup.assert, + 'process.assert() is deprecated. Please use the `assert` module instead.', + 'DEP0100'); - perThreadSetup.setupAllowedFlags(); + const perf = internalBinding('performance'); + const { + NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE, + } = perf.constants; + perf.markMilestone(NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE); startExecution(); } diff --git a/lib/internal/process/main_thread_only.js b/lib/internal/process/main_thread_only.js index 479ebd1433c8ff..862194ae46e27e 100644 --- a/lib/internal/process/main_thread_only.js +++ b/lib/internal/process/main_thread_only.js @@ -25,21 +25,25 @@ function setupStdio() { setupProcessStdio(getMainThreadStdio()); } -// Non-POSIX platforms like Windows don't have certain methods. -// Workers also lack these methods since they change process-global state. -function setupProcessMethods(_chdir, _umask) { - process.chdir = function chdir(directory) { +// The execution of this function itself should not cause any side effects. +function wrapProcessMethods(binding) { + function chdir(directory) { validateString(directory, 'directory'); - return _chdir(directory); - }; + return binding.chdir(directory); + } - process.umask = function umask(mask) { + function umask(mask) { if (mask === undefined) { // Get the mask - return _umask(mask); + return binding.umask(mask); } mask = validateMode(mask, 'mask'); - return _umask(mask); + return binding.umask(mask); + } + + return { + chdir, + umask }; } @@ -171,7 +175,7 @@ function setupChildProcessIpcChannel() { module.exports = { setupStdio, - setupProcessMethods, + wrapProcessMethods, setupSignalHandlers, setupChildProcessIpcChannel, wrapPosixCredentialSetters diff --git a/lib/internal/process/per_thread.js b/lib/internal/process/per_thread.js index 57cc9c38143f79..f1629e3a97c336 100644 --- a/lib/internal/process/per_thread.js +++ b/lib/internal/process/per_thread.js @@ -18,25 +18,30 @@ const { } = require('internal/errors'); const util = require('util'); const constants = internalBinding('constants').os.signals; -const { deprecate } = require('internal/util'); - -function setupAssert() { - process.assert = deprecate( - function(x, msg) { - if (!x) throw new ERR_ASSERTION(msg || 'assertion error'); - }, - 'process.assert() is deprecated. Please use the `assert` module instead.', - 'DEP0100'); + +function assert(x, msg) { + if (!x) throw new ERR_ASSERTION(msg || 'assertion error'); } -// Set up the process.cpuUsage() function. -function setupCpuUsage(_cpuUsage) { +// The execution of this function itself should not cause any side effects. +function wrapProcessMethods(binding, exceptionHandlerState) { + const { + hrtime: _hrtime, + hrtimeBigInt: _hrtimeBigInt, + cpuUsage: _cpuUsage, + memoryUsage: _memoryUsage + } = binding; + + function _rawDebug(...args) { + binding._rawDebug(util.format.apply(null, args)); + } + // Create the argument array that will be passed to the native function. const cpuValues = new Float64Array(2); // Replace the native function with the JS version that calls the native // function. - process.cpuUsage = function cpuUsage(prevValue) { + function cpuUsage(prevValue) { // If a previous value was passed in, ensure it has the correct shape. if (prevValue) { if (!previousValueIsValid(prevValue.user)) { @@ -80,7 +85,7 @@ function setupCpuUsage(_cpuUsage) { user: cpuValues[0], system: cpuValues[1] }; - }; + } // Ensure that a previously passed in value is valid. Currently, the native // implementation always returns numbers <= Number.MAX_SAFE_INTEGER. @@ -89,15 +94,13 @@ function setupCpuUsage(_cpuUsage) { num <= Number.MAX_SAFE_INTEGER && num >= 0; } -} -// The 3 entries filled in by the original process.hrtime contains -// the upper/lower 32 bits of the second part of the value, -// and the remaining nanoseconds of the value. -function setupHrtime(_hrtime, _hrtimeBigInt) { + // The 3 entries filled in by the original process.hrtime contains + // the upper/lower 32 bits of the second part of the value, + // and the remaining nanoseconds of the value. const hrValues = new Uint32Array(3); - process.hrtime = function hrtime(time) { + function hrtime(time) { _hrtime(hrValues); if (time !== undefined) { @@ -118,21 +121,18 @@ function setupHrtime(_hrtime, _hrtimeBigInt) { hrValues[0] * 0x100000000 + hrValues[1], hrValues[2] ]; - }; + } // Use a BigUint64Array in the closure because V8 does not have an API for // creating a BigInt out of a uint64_t yet. const hrBigintValues = new BigUint64Array(1); - process.hrtime.bigint = function() { + function hrtimeBigInt() { _hrtimeBigInt(hrBigintValues); return hrBigintValues[0]; - }; -} + } -function setupMemoryUsage(_memoryUsage) { const memValues = new Float64Array(4); - - process.memoryUsage = function memoryUsage() { + function memoryUsage() { _memoryUsage(memValues); return { rss: memValues[0], @@ -140,18 +140,9 @@ function setupMemoryUsage(_memoryUsage) { heapUsed: memValues[2], external: memValues[3] }; - }; -} - -function setupConfig() { - // Serialized config.gypi - process.config = JSON.parse(internalBinding('native_module').config); -} - - -function setupKillAndExit() { + } - process.exit = function(code) { + function exit(code) { if (code || code === 0) process.exitCode = code; @@ -159,10 +150,10 @@ function setupKillAndExit() { process._exiting = true; process.emit('exit', process.exitCode || 0); } - process.reallyExit(process.exitCode || 0); - }; + binding.reallyExit(process.exitCode || 0); + } - process.kill = function(pid, sig) { + function kill(pid, sig) { var err; if (process.env.NODE_V8_COVERAGE) { const { writeCoverage } = require('internal/process/coverage'); @@ -176,6 +167,8 @@ function setupKillAndExit() { // preserve null signal if (sig === (sig | 0)) { + // XXX(joyeecheung): we have to use process._kill here because + // it's monkey-patched by tests. err = process._kill(pid, sig); } else { sig = sig || 'SIGTERM'; @@ -190,22 +183,13 @@ function setupKillAndExit() { throw errnoException(err, 'kill'); return true; - }; -} - -function setupRawDebug(_rawDebug) { - process._rawDebug = function() { - _rawDebug(util.format.apply(null, arguments)); - }; -} - + } -function setupUncaughtExceptionCapture(exceptionHandlerState, - shouldAbortOnUncaughtToggle) { // shouldAbortOnUncaughtToggle is a typed array for faster // communication with JS. + const { shouldAbortOnUncaughtToggle } = binding; - process.setUncaughtExceptionCaptureCallback = function(fn) { + function setUncaughtExceptionCaptureCallback(fn) { if (fn === null) { exceptionHandlerState.captureFn = fn; shouldAbortOnUncaughtToggle[0] = 1; @@ -219,10 +203,22 @@ function setupUncaughtExceptionCapture(exceptionHandlerState, } exceptionHandlerState.captureFn = fn; shouldAbortOnUncaughtToggle[0] = 0; - }; + } - process.hasUncaughtExceptionCaptureCallback = function() { + function hasUncaughtExceptionCaptureCallback() { return exceptionHandlerState.captureFn !== null; + } + + return { + _rawDebug, + hrtime, + hrtimeBigInt, + cpuUsage, + memoryUsage, + kill, + exit, + setUncaughtExceptionCaptureCallback, + hasUncaughtExceptionCaptureCallback }; } @@ -326,38 +322,13 @@ function buildAllowedFlags() { Object.freeze(NodeEnvironmentFlagsSet.prototype.constructor); Object.freeze(NodeEnvironmentFlagsSet.prototype); - return process.allowedNodeEnvironmentFlags = Object.freeze( - new NodeEnvironmentFlagsSet( - allowedNodeEnvironmentFlags - )); -} - -function setupAllowedFlags() { - Object.defineProperty(process, 'allowedNodeEnvironmentFlags', { - get: buildAllowedFlags, - set(value) { - // If the user tries to set this to another value, override - // this completely to that value. - Object.defineProperty(this, 'allowedNodeEnvironmentFlags', { - value, - configurable: true, - enumerable: true, - writable: true - }); - }, - enumerable: true, - configurable: true - }); + return Object.freeze(new NodeEnvironmentFlagsSet( + allowedNodeEnvironmentFlags + )); } module.exports = { - setupAllowedFlags, - setupAssert, - setupCpuUsage, - setupHrtime, - setupMemoryUsage, - setupConfig, - setupKillAndExit, - setupRawDebug, - setupUncaughtExceptionCapture + assert, + buildAllowedFlags, + wrapProcessMethods }; diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index de3eca8ba464a2..b46ba5badab226 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -132,20 +132,6 @@ void SetupBootstrapObject(Environment* env, Local bootstrapper) { BOOTSTRAP_METHOD(_setupNextTick, SetupNextTick); BOOTSTRAP_METHOD(_setupPromises, SetupPromises); - BOOTSTRAP_METHOD(_chdir, Chdir); - BOOTSTRAP_METHOD(_cpuUsage, CPUUsage); - BOOTSTRAP_METHOD(_hrtime, Hrtime); - BOOTSTRAP_METHOD(_hrtimeBigInt, HrtimeBigInt); - BOOTSTRAP_METHOD(_memoryUsage, MemoryUsage); - BOOTSTRAP_METHOD(_rawDebug, RawDebug); - BOOTSTRAP_METHOD(_umask, Umask); - - Local should_abort_on_uncaught_toggle = - FIXED_ONE_BYTE_STRING(env->isolate(), "_shouldAbortOnUncaughtToggle"); - CHECK(bootstrapper->Set(env->context(), - should_abort_on_uncaught_toggle, - env->should_abort_on_uncaught_toggle().GetJSArray()) - .FromJust()); } #undef BOOTSTRAP_METHOD diff --git a/src/node.cc b/src/node.cc index 9975ff9d23f5b7..524785297eb503 100644 --- a/src/node.cc +++ b/src/node.cc @@ -95,8 +95,6 @@ #if defined(_MSC_VER) #include #include -#define umask _umask -typedef int mode_t; #else #include #include // getrlimit, setrlimit @@ -700,8 +698,7 @@ static void WaitForInspectorDisconnect(Environment* env) { #endif } - -static void Exit(const FunctionCallbackInfo& args) { +void Exit(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); WaitForInspectorDisconnect(env); v8_platform.StopTracingAgent(); @@ -1080,29 +1077,6 @@ void SetupProcessObject(Environment* env, DebugPortGetter, env->is_main_thread() ? DebugPortSetter : nullptr, env->as_external()).FromJust()); - - // define various internal methods - if (env->is_main_thread()) { - env->SetMethod(process, "_debugProcess", DebugProcess); - env->SetMethod(process, "_debugEnd", DebugEnd); - env->SetMethod(process, - "_startProfilerIdleNotifier", - StartProfilerIdleNotifier); - env->SetMethod(process, - "_stopProfilerIdleNotifier", - StopProfilerIdleNotifier); - env->SetMethod(process, "abort", Abort); - env->SetMethod(process, "chdir", Chdir); - env->SetMethod(process, "umask", Umask); - } - env->SetMethod(process, "_getActiveRequests", GetActiveRequests); - env->SetMethod(process, "_getActiveHandles", GetActiveHandles); - env->SetMethod(process, "_kill", Kill); - - env->SetMethodNoSideEffect(process, "cwd", Cwd); - env->SetMethod(process, "dlopen", binding::DLOpen); - env->SetMethod(process, "reallyExit", Exit); - env->SetMethodNoSideEffect(process, "uptime", Uptime); } @@ -1244,135 +1218,8 @@ void RegisterSignalHandler(int signal, CHECK_EQ(sigaction(signal, &sa, nullptr), 0); } - -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 -} - - inline void PlatformInit() { #ifdef __POSIX__ #if HAVE_INSPECTOR diff --git a/src/node_binding.cc b/src/node_binding.cc index 0758741ffcb852..46ae5f6840f658 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -44,6 +44,7 @@ V(performance) \ V(pipe_wrap) \ V(process_wrap) \ + V(process_methods) \ V(serdes) \ V(signal_wrap) \ V(spawn_sync) \ diff --git a/src/node_internals.h b/src/node_internals.h index 0d07eb5d907b57..46a6c9e3e0fb6b 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -119,6 +119,7 @@ void GetSockOrPeerName(const v8::FunctionCallbackInfo& args) { args.GetReturnValue().Set(err); } +void Exit(const v8::FunctionCallbackInfo& args); void SignalExit(int signo); #ifdef __POSIX__ void RegisterSignalHandler(int signal, @@ -699,21 +700,7 @@ static inline const char* errno_string(int errorno) { extern double prog_start_time; -void Abort(const v8::FunctionCallbackInfo& args); -void Chdir(const v8::FunctionCallbackInfo& args); -void CPUUsage(const v8::FunctionCallbackInfo& args); -void Cwd(const v8::FunctionCallbackInfo& args); -void GetActiveHandles(const v8::FunctionCallbackInfo& args); -void GetActiveRequests(const v8::FunctionCallbackInfo& args); -void Hrtime(const v8::FunctionCallbackInfo& args); -void HrtimeBigInt(const v8::FunctionCallbackInfo& args); -void Kill(const v8::FunctionCallbackInfo& args); -void MemoryUsage(const v8::FunctionCallbackInfo& args); void RawDebug(const v8::FunctionCallbackInfo& args); -void StartProfilerIdleNotifier(const v8::FunctionCallbackInfo& args); -void StopProfilerIdleNotifier(const v8::FunctionCallbackInfo& args); -void Umask(const v8::FunctionCallbackInfo& args); -void Uptime(const v8::FunctionCallbackInfo& args); void DebugPortGetter(v8::Local property, const v8::PropertyCallbackInfo& info); diff --git a/src/node_process.cc b/src/node_process.cc index b9376953e241be..477ac2adc8eb6c 100644 --- a/src/node_process.cc +++ b/src/node_process.cc @@ -8,6 +8,8 @@ #include "uv.h" #include "v8.h" +#include + #if HAVE_INSPECTOR #include "inspector_io.h" #endif @@ -36,10 +38,12 @@ 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; @@ -61,11 +65,11 @@ Mutex environ_mutex; #define CHDIR_BUFSIZE (PATH_MAX) #endif -void Abort(const FunctionCallbackInfo& args) { +static void Abort(const FunctionCallbackInfo& args) { Abort(); } -void Chdir(const FunctionCallbackInfo& args) { +static void Chdir(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(env->is_main_thread()); @@ -88,7 +92,7 @@ void Chdir(const FunctionCallbackInfo& args) { // 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. -void CPUUsage(const FunctionCallbackInfo& args) { +static void CPUUsage(const FunctionCallbackInfo& args) { uv_rusage_t rusage; // Call libuv to get the values we'll return. @@ -111,7 +115,7 @@ void CPUUsage(const FunctionCallbackInfo& args) { fields[1] = MICROS_PER_SEC * rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec; } -void Cwd(const FunctionCallbackInfo& args) { +static void Cwd(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); char buf[CHDIR_BUFSIZE]; size_t cwd_len = sizeof(buf); @@ -138,7 +142,7 @@ void Cwd(const FunctionCallbackInfo& args) { // 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. -void Hrtime(const FunctionCallbackInfo& args) { +static void Hrtime(const FunctionCallbackInfo& args) { uint64_t t = uv_hrtime(); Local ab = args[0].As()->Buffer(); @@ -149,13 +153,13 @@ void Hrtime(const FunctionCallbackInfo& args) { fields[2] = t % NANOS_PER_SEC; } -void HrtimeBigInt(const FunctionCallbackInfo& args) { +static void HrtimeBigInt(const FunctionCallbackInfo& args) { Local ab = args[0].As()->Buffer(); uint64_t* fields = static_cast(ab->GetContents().Data()); fields[0] = uv_hrtime(); } -void Kill(const FunctionCallbackInfo& args) { +static void Kill(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Local context = env->context(); @@ -170,8 +174,7 @@ void Kill(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(err); } - -void MemoryUsage(const FunctionCallbackInfo& args) { +static void MemoryUsage(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); size_t rss; @@ -209,18 +212,17 @@ void RawDebug(const FunctionCallbackInfo& args) { fflush(stderr); } -void StartProfilerIdleNotifier(const FunctionCallbackInfo& args) { +static void StartProfilerIdleNotifier(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); env->StartProfilerIdleNotifier(); } - -void StopProfilerIdleNotifier(const FunctionCallbackInfo& args) { +static void StopProfilerIdleNotifier(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); env->StopProfilerIdleNotifier(); } -void Umask(const FunctionCallbackInfo& args) { +static void Umask(const FunctionCallbackInfo& args) { uint32_t old; CHECK_EQ(args.Length(), 1); @@ -237,7 +239,7 @@ void Umask(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(old); } -void Uptime(const FunctionCallbackInfo& args) { +static void Uptime(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); double uptime; @@ -255,7 +257,6 @@ void ProcessTitleGetter(Local property, NewStringType::kNormal).ToLocalChecked()); } - void ProcessTitleSetter(Local property, Local value, const PropertyCallbackInfo& info) { @@ -270,7 +271,7 @@ void GetParentProcessId(Local property, info.GetReturnValue().Set(uv_os_getppid()); } -void GetActiveRequests(const FunctionCallbackInfo& args) { +static void GetActiveRequests(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); std::vector> request_v; @@ -315,5 +316,159 @@ void DebugPortSetter(Local property, 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); + + Local should_abort_on_uncaught_toggle = + FIXED_ONE_BYTE_STRING(env->isolate(), "shouldAbortOnUncaughtToggle"); + CHECK(target + ->Set(env->context(), + should_abort_on_uncaught_toggle, + env->should_abort_on_uncaught_toggle().GetJSArray()) + .FromJust()); +} } // namespace node + +NODE_MODULE_CONTEXT_AWARE_INTERNAL(process_methods, + node::InitializeProcessMethods) diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index 9816aa05466ff3..905be701e9f1a1 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -9,7 +9,7 @@ const common = require('../common'); const assert = require('assert'); const isMainThread = common.isMainThread; -const kMaxModuleCount = isMainThread ? 61 : 82; +const kMaxModuleCount = isMainThread ? 62 : 82; assert(list.length <= kMaxModuleCount, `Total length: ${list.length}\n` + list.join('\n') From 5b780eb8132907d58629c8f419fafbf4431a549b Mon Sep 17 00:00:00 2001 From: cjihrig Date: Fri, 21 Dec 2018 09:10:34 -0500 Subject: [PATCH 2/8] src: fix compiler warnings The warnings in question are: ../src/node.cc:844:13: warning: unused function 'DebugProcess' [-Wunused-function] static void DebugProcess(const FunctionCallbackInfo& args); ^ ../src/node.cc:845:13: warning: unused function 'DebugEnd' [-Wunused-function] static void DebugEnd(const FunctionCallbackInfo& args); PR-URL: https://github.com/nodejs/node/pull/25165 Reviewed-By: Anna Henningsen Reviewed-By: Luigi Pinca Reviewed-By: Richard Lau Reviewed-By: Daniel Bevenius --- src/node.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/node.cc b/src/node.cc index 524785297eb503..01da762ebddb26 100644 --- a/src/node.cc +++ b/src/node.cc @@ -852,9 +852,6 @@ static Local GetFeatures(Environment* env) { return scope.Escape(obj); } -static void DebugProcess(const FunctionCallbackInfo& args); -static void DebugEnd(const FunctionCallbackInfo& args); - void SetupProcessObject(Environment* env, const std::vector& args, const std::vector& exec_args) { From 172466cfde90bb66700ba0a10c30b92b8478a925 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Sun, 30 Dec 2018 19:44:13 -0500 Subject: [PATCH 3/8] coverage: use process._rawDebug() during setup console is not ready to use at this point in the bootstrapping process, so switch to process._rawDebug() instead. PR-URL: https://github.com/nodejs/node/pull/25289 Fixes: https://github.com/nodejs/node/issues/25287 Reviewed-By: Anna Henningsen Reviewed-By: Gus Caplan Reviewed-By: Yuta Hiroto Reviewed-By: Joyee Cheung Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- lib/internal/process/coverage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/process/coverage.js b/lib/internal/process/coverage.js index 5c5d0c2b6171f3..c84181db933334 100644 --- a/lib/internal/process/coverage.js +++ b/lib/internal/process/coverage.js @@ -53,7 +53,7 @@ exports.writeCoverage = writeCoverage; function setup() { const { Connection } = internalBinding('inspector'); if (!Connection) { - console.warn('inspector not enabled'); + process._rawDebug('inspector not enabled'); return; } @@ -80,7 +80,7 @@ function setup() { coverageDirectory = process.env.NODE_V8_COVERAGE = resolve(process.env.NODE_V8_COVERAGE); } catch (err) { - console.error(err); + process._rawDebug(err.toString()); } } From dcd77672fd4bbb878666af1a35e7eee3448628a4 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Sun, 30 Dec 2018 19:47:47 -0500 Subject: [PATCH 4/8] coverage: pass cwd to path.resolve() in setup During coverage setup, path.resolve() is called. path.resolve() can potentially call process.cwd(), which hasn't been bootstrapped yet. This commit passes the current working directory directly so that path.resolve() doesn't attempt to compute it. PR-URL: https://github.com/nodejs/node/pull/25289 Fixes: https://github.com/nodejs/node/issues/25287 Reviewed-By: Anna Henningsen Reviewed-By: Gus Caplan Reviewed-By: Yuta Hiroto Reviewed-By: Joyee Cheung Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- lib/internal/process/coverage.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/internal/process/coverage.js b/lib/internal/process/coverage.js index c84181db933334..ef37deba8a5f58 100644 --- a/lib/internal/process/coverage.js +++ b/lib/internal/process/coverage.js @@ -76,9 +76,10 @@ function setup() { })); try { + const { cwd } = internalBinding('process_methods'); const { resolve } = require('path'); coverageDirectory = process.env.NODE_V8_COVERAGE = - resolve(process.env.NODE_V8_COVERAGE); + resolve(cwd(), process.env.NODE_V8_COVERAGE); } catch (err) { process._rawDebug(err.toString()); } From 066985ab82af178b029d66447d7be4cb8794c314 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Mon, 31 Dec 2018 11:10:01 -0500 Subject: [PATCH 5/8] test: make test-v8-coverage.js more strict Update the coverage test to verify that nothing is printed to stderr (which happens when coverage errors happen). Also add a test case to verify that non-absolute coverage paths work. PR-URL: https://github.com/nodejs/node/pull/25289 Fixes: https://github.com/nodejs/node/issues/25287 Reviewed-By: Anna Henningsen Reviewed-By: Gus Caplan Reviewed-By: Yuta Hiroto Reviewed-By: Joyee Cheung Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- test/parallel/test-v8-coverage.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/parallel/test-v8-coverage.js b/test/parallel/test-v8-coverage.js index 1cb5e70b01cbf9..a7a318c34d4e96 100644 --- a/test/parallel/test-v8-coverage.js +++ b/test/parallel/test-v8-coverage.js @@ -23,6 +23,7 @@ function nextdir() { require.resolve('../fixtures/v8-coverage/basic') ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); assert.strictEqual(output.status, 0); + assert.strictEqual(output.stderr.toString(), ''); const fixtureCoverage = getFixtureCoverage('basic.js', coverageDirectory); assert.ok(fixtureCoverage); // first branch executed. @@ -38,6 +39,7 @@ function nextdir() { require.resolve('../fixtures/v8-coverage/exit-1') ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); assert.strictEqual(output.status, 1); + assert.strictEqual(output.stderr.toString(), ''); const fixtureCoverage = getFixtureCoverage('exit-1.js', coverageDirectory); assert.ok(fixtureCoverage, 'coverage not found for file'); // first branch executed. @@ -55,6 +57,7 @@ function nextdir() { if (!common.isWindows) { assert.strictEqual(output.signal, 'SIGINT'); } + assert.strictEqual(output.stderr.toString(), ''); const fixtureCoverage = getFixtureCoverage('sigint.js', coverageDirectory); assert.ok(fixtureCoverage); // first branch executed. @@ -70,6 +73,7 @@ function nextdir() { require.resolve('../fixtures/v8-coverage/spawn-subprocess') ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); assert.strictEqual(output.status, 0); + assert.strictEqual(output.stderr.toString(), ''); const fixtureCoverage = getFixtureCoverage('subprocess.js', coverageDirectory); assert.ok(fixtureCoverage); @@ -86,6 +90,7 @@ function nextdir() { require.resolve('../fixtures/v8-coverage/worker') ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); assert.strictEqual(output.status, 0); + assert.strictEqual(output.stderr.toString(), ''); const fixtureCoverage = getFixtureCoverage('subprocess.js', coverageDirectory); assert.ok(fixtureCoverage); @@ -102,6 +107,7 @@ function nextdir() { require.resolve('../fixtures/v8-coverage/spawn-subprocess-no-cov') ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); assert.strictEqual(output.status, 0); + assert.strictEqual(output.stderr.toString(), ''); const fixtureCoverage = getFixtureCoverage('subprocess.js', coverageDirectory); assert.strictEqual(fixtureCoverage, undefined); @@ -114,6 +120,7 @@ function nextdir() { require.resolve('../fixtures/v8-coverage/async-hooks') ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); assert.strictEqual(output.status, 0); + assert.strictEqual(output.stderr.toString(), ''); const fixtureCoverage = getFixtureCoverage('async-hooks.js', coverageDirectory); assert.ok(fixtureCoverage); @@ -121,6 +128,27 @@ function nextdir() { assert.strictEqual(fixtureCoverage.functions[1].ranges[0].count, 1); } +// Outputs coverage when the coverage directory is not absolute. +{ + const coverageDirectory = nextdir(); + const absoluteCoverageDirectory = path.join(tmpdir.path, coverageDirectory); + const output = spawnSync(process.execPath, [ + require.resolve('../fixtures/v8-coverage/basic') + ], { + cwd: tmpdir.path, + env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } + }); + assert.strictEqual(output.status, 0); + assert.strictEqual(output.stderr.toString(), ''); + const fixtureCoverage = getFixtureCoverage('basic.js', + absoluteCoverageDirectory); + assert.ok(fixtureCoverage); + // first branch executed. + assert.strictEqual(fixtureCoverage.functions[1].ranges[0].count, 1); + // second branch did not execute. + assert.strictEqual(fixtureCoverage.functions[1].ranges[1].count, 0); +} + // Extracts the coverage object for a given fixture name. function getFixtureCoverage(fixtureFile, coverageDirectory) { const coverageFiles = fs.readdirSync(coverageDirectory); From 5771dc411c1b50433ac7fc1ed84b526e698b0469 Mon Sep 17 00:00:00 2001 From: Ankur Oberoi Date: Mon, 17 Dec 2018 17:18:55 -0800 Subject: [PATCH 6/8] util: fixes type in argument type validation error PR-URL: https://github.com/nodejs/node/pull/25103 Reviewed-By: Luigi Pinca Reviewed-By: Anna Henningsen --- lib/util.js | 2 +- test/parallel/test-util-inherits.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/util.js b/lib/util.js index 624f4dfb7d032b..82c92ad0be47cf 100644 --- a/lib/util.js +++ b/lib/util.js @@ -303,7 +303,7 @@ function inherits(ctor, superCtor) { if (superCtor.prototype === undefined) { throw new ERR_INVALID_ARG_TYPE('superCtor.prototype', - 'Function', superCtor.prototype); + 'Object', superCtor.prototype); } Object.defineProperty(ctor, 'super_', { value: superCtor, diff --git a/test/parallel/test-util-inherits.js b/test/parallel/test-util-inherits.js index 9bbb4352dc3853..ed0800c33cec42 100644 --- a/test/parallel/test-util-inherits.js +++ b/test/parallel/test-util-inherits.js @@ -88,7 +88,7 @@ common.expectsError(function() { }, { code: 'ERR_INVALID_ARG_TYPE', type: TypeError, - message: 'The "superCtor.prototype" property must be of type Function. ' + + message: 'The "superCtor.prototype" property must be of type Object. ' + 'Received type undefined' }); From 7dffd82f96aed0c0353aee05918a834f0957492e Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 8 Jan 2019 23:08:42 +0800 Subject: [PATCH 7/8] src: move node::errno_string into node_errors.h/cc Move `node::errno_string` into node_errors.h/cc and move it into the `node:errors` namespace to reduce the size of the header. It's not on any performance-critical code path so does not need to be inlined. PR-URL: https://github.com/nodejs/node/pull/25396 Reviewed-By: James M Snell Reviewed-By: Anna Henningsen --- src/env.cc | 2 +- src/exceptions.cc | 10 +- src/node_errors.cc | 330 +++++++++++++++++++++++++++++++++++++++++++ src/node_errors.h | 3 +- src/node_internals.h | 326 ------------------------------------------ 5 files changed, 339 insertions(+), 332 deletions(-) diff --git a/src/env.cc b/src/env.cc index 4f6c6974c50f3f..765aac1b983a8b 100644 --- a/src/env.cc +++ b/src/env.cc @@ -819,7 +819,7 @@ void Environment::CollectExceptionInfo(Local object, return; Local obj = object.As(); - const char* err_string = node::errno_string(errorno); + const char* err_string = errors::errno_string(errorno); if (message == nullptr || message[0] == '\0') { message = strerror(errorno); diff --git a/src/exceptions.cc b/src/exceptions.cc index c0804798e1aad6..d5c05fdf420926 100644 --- a/src/exceptions.cc +++ b/src/exceptions.cc @@ -1,9 +1,11 @@ -#include "node.h" -#include "node_internals.h" +// This file contains implementation of error APIs exposed in node.h + #include "env-inl.h" +#include "node.h" +#include "node_errors.h" #include "util-inl.h" -#include "v8.h" #include "uv.h" +#include "v8.h" #include @@ -27,7 +29,7 @@ Local ErrnoException(Isolate* isolate, Environment* env = Environment::GetCurrent(isolate); Local e; - Local estring = OneByteString(isolate, errno_string(errorno)); + Local estring = OneByteString(isolate, errors::errno_string(errorno)); if (msg == nullptr || msg[0] == '\0') { msg = strerror(errorno); } diff --git a/src/node_errors.cc b/src/node_errors.cc index 7775e36b69bc82..812a87f229fbc7 100644 --- a/src/node_errors.cc +++ b/src/node_errors.cc @@ -1,4 +1,6 @@ +#include #include + #include "node_errors.h" #include "node_internals.h" @@ -322,6 +324,334 @@ TryCatchScope::~TryCatchScope() { } } +const char* errno_string(int errorno) { +#define ERRNO_CASE(e) \ + case e: \ + return #e; + switch (errorno) { +#ifdef EACCES + ERRNO_CASE(EACCES); +#endif + +#ifdef EADDRINUSE + ERRNO_CASE(EADDRINUSE); +#endif + +#ifdef EADDRNOTAVAIL + ERRNO_CASE(EADDRNOTAVAIL); +#endif + +#ifdef EAFNOSUPPORT + ERRNO_CASE(EAFNOSUPPORT); +#endif + +#ifdef EAGAIN + ERRNO_CASE(EAGAIN); +#endif + +#ifdef EWOULDBLOCK +#if EAGAIN != EWOULDBLOCK + ERRNO_CASE(EWOULDBLOCK); +#endif +#endif + +#ifdef EALREADY + ERRNO_CASE(EALREADY); +#endif + +#ifdef EBADF + ERRNO_CASE(EBADF); +#endif + +#ifdef EBADMSG + ERRNO_CASE(EBADMSG); +#endif + +#ifdef EBUSY + ERRNO_CASE(EBUSY); +#endif + +#ifdef ECANCELED + ERRNO_CASE(ECANCELED); +#endif + +#ifdef ECHILD + ERRNO_CASE(ECHILD); +#endif + +#ifdef ECONNABORTED + ERRNO_CASE(ECONNABORTED); +#endif + +#ifdef ECONNREFUSED + ERRNO_CASE(ECONNREFUSED); +#endif + +#ifdef ECONNRESET + ERRNO_CASE(ECONNRESET); +#endif + +#ifdef EDEADLK + ERRNO_CASE(EDEADLK); +#endif + +#ifdef EDESTADDRREQ + ERRNO_CASE(EDESTADDRREQ); +#endif + +#ifdef EDOM + ERRNO_CASE(EDOM); +#endif + +#ifdef EDQUOT + ERRNO_CASE(EDQUOT); +#endif + +#ifdef EEXIST + ERRNO_CASE(EEXIST); +#endif + +#ifdef EFAULT + ERRNO_CASE(EFAULT); +#endif + +#ifdef EFBIG + ERRNO_CASE(EFBIG); +#endif + +#ifdef EHOSTUNREACH + ERRNO_CASE(EHOSTUNREACH); +#endif + +#ifdef EIDRM + ERRNO_CASE(EIDRM); +#endif + +#ifdef EILSEQ + ERRNO_CASE(EILSEQ); +#endif + +#ifdef EINPROGRESS + ERRNO_CASE(EINPROGRESS); +#endif + +#ifdef EINTR + ERRNO_CASE(EINTR); +#endif + +#ifdef EINVAL + ERRNO_CASE(EINVAL); +#endif + +#ifdef EIO + ERRNO_CASE(EIO); +#endif + +#ifdef EISCONN + ERRNO_CASE(EISCONN); +#endif + +#ifdef EISDIR + ERRNO_CASE(EISDIR); +#endif + +#ifdef ELOOP + ERRNO_CASE(ELOOP); +#endif + +#ifdef EMFILE + ERRNO_CASE(EMFILE); +#endif + +#ifdef EMLINK + ERRNO_CASE(EMLINK); +#endif + +#ifdef EMSGSIZE + ERRNO_CASE(EMSGSIZE); +#endif + +#ifdef EMULTIHOP + ERRNO_CASE(EMULTIHOP); +#endif + +#ifdef ENAMETOOLONG + ERRNO_CASE(ENAMETOOLONG); +#endif + +#ifdef ENETDOWN + ERRNO_CASE(ENETDOWN); +#endif + +#ifdef ENETRESET + ERRNO_CASE(ENETRESET); +#endif + +#ifdef ENETUNREACH + ERRNO_CASE(ENETUNREACH); +#endif + +#ifdef ENFILE + ERRNO_CASE(ENFILE); +#endif + +#ifdef ENOBUFS + ERRNO_CASE(ENOBUFS); +#endif + +#ifdef ENODATA + ERRNO_CASE(ENODATA); +#endif + +#ifdef ENODEV + ERRNO_CASE(ENODEV); +#endif + +#ifdef ENOENT + ERRNO_CASE(ENOENT); +#endif + +#ifdef ENOEXEC + ERRNO_CASE(ENOEXEC); +#endif + +#ifdef ENOLINK + ERRNO_CASE(ENOLINK); +#endif + +#ifdef ENOLCK +#if ENOLINK != ENOLCK + ERRNO_CASE(ENOLCK); +#endif +#endif + +#ifdef ENOMEM + ERRNO_CASE(ENOMEM); +#endif + +#ifdef ENOMSG + ERRNO_CASE(ENOMSG); +#endif + +#ifdef ENOPROTOOPT + ERRNO_CASE(ENOPROTOOPT); +#endif + +#ifdef ENOSPC + ERRNO_CASE(ENOSPC); +#endif + +#ifdef ENOSR + ERRNO_CASE(ENOSR); +#endif + +#ifdef ENOSTR + ERRNO_CASE(ENOSTR); +#endif + +#ifdef ENOSYS + ERRNO_CASE(ENOSYS); +#endif + +#ifdef ENOTCONN + ERRNO_CASE(ENOTCONN); +#endif + +#ifdef ENOTDIR + ERRNO_CASE(ENOTDIR); +#endif + +#ifdef ENOTEMPTY +#if ENOTEMPTY != EEXIST + ERRNO_CASE(ENOTEMPTY); +#endif +#endif + +#ifdef ENOTSOCK + ERRNO_CASE(ENOTSOCK); +#endif + +#ifdef ENOTSUP + ERRNO_CASE(ENOTSUP); +#else +#ifdef EOPNOTSUPP + ERRNO_CASE(EOPNOTSUPP); +#endif +#endif + +#ifdef ENOTTY + ERRNO_CASE(ENOTTY); +#endif + +#ifdef ENXIO + ERRNO_CASE(ENXIO); +#endif + +#ifdef EOVERFLOW + ERRNO_CASE(EOVERFLOW); +#endif + +#ifdef EPERM + ERRNO_CASE(EPERM); +#endif + +#ifdef EPIPE + ERRNO_CASE(EPIPE); +#endif + +#ifdef EPROTO + ERRNO_CASE(EPROTO); +#endif + +#ifdef EPROTONOSUPPORT + ERRNO_CASE(EPROTONOSUPPORT); +#endif + +#ifdef EPROTOTYPE + ERRNO_CASE(EPROTOTYPE); +#endif + +#ifdef ERANGE + ERRNO_CASE(ERANGE); +#endif + +#ifdef EROFS + ERRNO_CASE(EROFS); +#endif + +#ifdef ESPIPE + ERRNO_CASE(ESPIPE); +#endif + +#ifdef ESRCH + ERRNO_CASE(ESRCH); +#endif + +#ifdef ESTALE + ERRNO_CASE(ESTALE); +#endif + +#ifdef ETIME + ERRNO_CASE(ETIME); +#endif + +#ifdef ETIMEDOUT + ERRNO_CASE(ETIMEDOUT); +#endif + +#ifdef ETXTBSY + ERRNO_CASE(ETXTBSY); +#endif + +#ifdef EXDEV + ERRNO_CASE(EXDEV); +#endif + + default: + return ""; + } +} + } // namespace errors void DecorateErrorStack(Environment* env, diff --git a/src/node_errors.h b/src/node_errors.h index a65a45257f5ee8..eefc5706374298 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -188,11 +188,12 @@ class TryCatchScope : public v8::TryCatch { CatchMode mode_; }; +const char* errno_string(int errorno); + } // namespace errors void DecorateErrorStack(Environment* env, const errors::TryCatchScope& try_catch); - } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/node_internals.h b/src/node_internals.h index 46a6c9e3e0fb6b..e7282457a7052b 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -361,332 +361,6 @@ int ThreadPoolWork::CancelWork() { tracing::AgentWriterHandle* GetTracingAgentWriter(); void DisposePlatform(); -static inline const char* errno_string(int errorno) { -#define ERRNO_CASE(e) case e: return #e; - switch (errorno) { -#ifdef EACCES - ERRNO_CASE(EACCES); -#endif - -#ifdef EADDRINUSE - ERRNO_CASE(EADDRINUSE); -#endif - -#ifdef EADDRNOTAVAIL - ERRNO_CASE(EADDRNOTAVAIL); -#endif - -#ifdef EAFNOSUPPORT - ERRNO_CASE(EAFNOSUPPORT); -#endif - -#ifdef EAGAIN - ERRNO_CASE(EAGAIN); -#endif - -#ifdef EWOULDBLOCK -# if EAGAIN != EWOULDBLOCK - ERRNO_CASE(EWOULDBLOCK); -# endif -#endif - -#ifdef EALREADY - ERRNO_CASE(EALREADY); -#endif - -#ifdef EBADF - ERRNO_CASE(EBADF); -#endif - -#ifdef EBADMSG - ERRNO_CASE(EBADMSG); -#endif - -#ifdef EBUSY - ERRNO_CASE(EBUSY); -#endif - -#ifdef ECANCELED - ERRNO_CASE(ECANCELED); -#endif - -#ifdef ECHILD - ERRNO_CASE(ECHILD); -#endif - -#ifdef ECONNABORTED - ERRNO_CASE(ECONNABORTED); -#endif - -#ifdef ECONNREFUSED - ERRNO_CASE(ECONNREFUSED); -#endif - -#ifdef ECONNRESET - ERRNO_CASE(ECONNRESET); -#endif - -#ifdef EDEADLK - ERRNO_CASE(EDEADLK); -#endif - -#ifdef EDESTADDRREQ - ERRNO_CASE(EDESTADDRREQ); -#endif - -#ifdef EDOM - ERRNO_CASE(EDOM); -#endif - -#ifdef EDQUOT - ERRNO_CASE(EDQUOT); -#endif - -#ifdef EEXIST - ERRNO_CASE(EEXIST); -#endif - -#ifdef EFAULT - ERRNO_CASE(EFAULT); -#endif - -#ifdef EFBIG - ERRNO_CASE(EFBIG); -#endif - -#ifdef EHOSTUNREACH - ERRNO_CASE(EHOSTUNREACH); -#endif - -#ifdef EIDRM - ERRNO_CASE(EIDRM); -#endif - -#ifdef EILSEQ - ERRNO_CASE(EILSEQ); -#endif - -#ifdef EINPROGRESS - ERRNO_CASE(EINPROGRESS); -#endif - -#ifdef EINTR - ERRNO_CASE(EINTR); -#endif - -#ifdef EINVAL - ERRNO_CASE(EINVAL); -#endif - -#ifdef EIO - ERRNO_CASE(EIO); -#endif - -#ifdef EISCONN - ERRNO_CASE(EISCONN); -#endif - -#ifdef EISDIR - ERRNO_CASE(EISDIR); -#endif - -#ifdef ELOOP - ERRNO_CASE(ELOOP); -#endif - -#ifdef EMFILE - ERRNO_CASE(EMFILE); -#endif - -#ifdef EMLINK - ERRNO_CASE(EMLINK); -#endif - -#ifdef EMSGSIZE - ERRNO_CASE(EMSGSIZE); -#endif - -#ifdef EMULTIHOP - ERRNO_CASE(EMULTIHOP); -#endif - -#ifdef ENAMETOOLONG - ERRNO_CASE(ENAMETOOLONG); -#endif - -#ifdef ENETDOWN - ERRNO_CASE(ENETDOWN); -#endif - -#ifdef ENETRESET - ERRNO_CASE(ENETRESET); -#endif - -#ifdef ENETUNREACH - ERRNO_CASE(ENETUNREACH); -#endif - -#ifdef ENFILE - ERRNO_CASE(ENFILE); -#endif - -#ifdef ENOBUFS - ERRNO_CASE(ENOBUFS); -#endif - -#ifdef ENODATA - ERRNO_CASE(ENODATA); -#endif - -#ifdef ENODEV - ERRNO_CASE(ENODEV); -#endif - -#ifdef ENOENT - ERRNO_CASE(ENOENT); -#endif - -#ifdef ENOEXEC - ERRNO_CASE(ENOEXEC); -#endif - -#ifdef ENOLINK - ERRNO_CASE(ENOLINK); -#endif - -#ifdef ENOLCK -# if ENOLINK != ENOLCK - ERRNO_CASE(ENOLCK); -# endif -#endif - -#ifdef ENOMEM - ERRNO_CASE(ENOMEM); -#endif - -#ifdef ENOMSG - ERRNO_CASE(ENOMSG); -#endif - -#ifdef ENOPROTOOPT - ERRNO_CASE(ENOPROTOOPT); -#endif - -#ifdef ENOSPC - ERRNO_CASE(ENOSPC); -#endif - -#ifdef ENOSR - ERRNO_CASE(ENOSR); -#endif - -#ifdef ENOSTR - ERRNO_CASE(ENOSTR); -#endif - -#ifdef ENOSYS - ERRNO_CASE(ENOSYS); -#endif - -#ifdef ENOTCONN - ERRNO_CASE(ENOTCONN); -#endif - -#ifdef ENOTDIR - ERRNO_CASE(ENOTDIR); -#endif - -#ifdef ENOTEMPTY -# if ENOTEMPTY != EEXIST - ERRNO_CASE(ENOTEMPTY); -# endif -#endif - -#ifdef ENOTSOCK - ERRNO_CASE(ENOTSOCK); -#endif - -#ifdef ENOTSUP - ERRNO_CASE(ENOTSUP); -#else -# ifdef EOPNOTSUPP - ERRNO_CASE(EOPNOTSUPP); -# endif -#endif - -#ifdef ENOTTY - ERRNO_CASE(ENOTTY); -#endif - -#ifdef ENXIO - ERRNO_CASE(ENXIO); -#endif - - -#ifdef EOVERFLOW - ERRNO_CASE(EOVERFLOW); -#endif - -#ifdef EPERM - ERRNO_CASE(EPERM); -#endif - -#ifdef EPIPE - ERRNO_CASE(EPIPE); -#endif - -#ifdef EPROTO - ERRNO_CASE(EPROTO); -#endif - -#ifdef EPROTONOSUPPORT - ERRNO_CASE(EPROTONOSUPPORT); -#endif - -#ifdef EPROTOTYPE - ERRNO_CASE(EPROTOTYPE); -#endif - -#ifdef ERANGE - ERRNO_CASE(ERANGE); -#endif - -#ifdef EROFS - ERRNO_CASE(EROFS); -#endif - -#ifdef ESPIPE - ERRNO_CASE(ESPIPE); -#endif - -#ifdef ESRCH - ERRNO_CASE(ESRCH); -#endif - -#ifdef ESTALE - ERRNO_CASE(ESTALE); -#endif - -#ifdef ETIME - ERRNO_CASE(ETIME); -#endif - -#ifdef ETIMEDOUT - ERRNO_CASE(ETIMEDOUT); -#endif - -#ifdef ETXTBSY - ERRNO_CASE(ETXTBSY); -#endif - -#ifdef EXDEV - ERRNO_CASE(EXDEV); -#endif - - default: return ""; - } -} - #define TRACING_CATEGORY_NODE "node" #define TRACING_CATEGORY_NODE1(one) \ TRACING_CATEGORY_NODE "," \ From 7bf10e341345d2cd3105e36b5977428bfad411a9 Mon Sep 17 00:00:00 2001 From: ZYSzys <17367077526@163.com> Date: Fri, 11 Jan 2019 10:30:00 +0800 Subject: [PATCH 8/8] test: add test for fs.lchmod PR-URL: https://github.com/nodejs/node/pull/25439 Reviewed-By: Luigi Pinca Reviewed-By: Colin Ihrig Reviewed-By: Masashi Hirano Reviewed-By: James M Snell --- test/parallel/test-fs-lchmod.js | 67 +++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 test/parallel/test-fs-lchmod.js diff --git a/test/parallel/test-fs-lchmod.js b/test/parallel/test-fs-lchmod.js new file mode 100644 index 00000000000000..3238790152b70e --- /dev/null +++ b/test/parallel/test-fs-lchmod.js @@ -0,0 +1,67 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const util = require('util'); +const fs = require('fs'); +const { promises } = fs; +const f = __filename; + +// This test ensures that input for lchmod is valid, testing for valid +// inputs for path, mode and callback + +if (!common.isOSX) { + common.skip('lchmod is only available on macOS'); +} + +// Check callback +assert.throws(() => fs.lchmod(f), { code: 'ERR_INVALID_CALLBACK' }); +assert.throws(() => fs.lchmod(), { code: 'ERR_INVALID_CALLBACK' }); +assert.throws(() => fs.lchmod(f, {}), { code: 'ERR_INVALID_CALLBACK' }); + +// Check path +[false, 1, {}, [], null, undefined].forEach((i) => { + common.expectsError( + () => fs.lchmod(i, 0o777, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError + } + ); + common.expectsError( + () => fs.lchmodSync(i), + { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError + } + ); +}); + +// Check mode +[false, null, undefined, {}, [], '', '123x'].forEach((input) => { + const errObj = { + code: 'ERR_INVALID_ARG_VALUE', + name: 'TypeError [ERR_INVALID_ARG_VALUE]', + message: 'The argument \'mode\' must be a 32-bit unsigned integer or an ' + + `octal string. Received ${util.inspect(input)}` + }; + + promises.lchmod(f, input, () => {}) + .then(common.mustNotCall()) + .catch(common.expectsError(errObj)); + assert.throws(() => fs.lchmodSync(f, input), errObj); +}); + +[-1, 2 ** 32].forEach((input) => { + const errObj = { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError [ERR_OUT_OF_RANGE]', + message: 'The value of "mode" is out of range. It must be >= 0 && < ' + + `4294967296. Received ${input}` + }; + + promises.lchmod(f, input, () => {}) + .then(common.mustNotCall()) + .catch(common.expectsError(errObj)); + assert.throws(() => fs.lchmodSync(f, input), errObj); +});