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/coverage.js b/lib/internal/process/coverage.js index 5c5d0c2b6171f3..ef37deba8a5f58 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; } @@ -76,11 +76,12 @@ 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) { - console.error(err); + process._rawDebug(err.toString()); } } 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/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/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/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.cc b/src/node.cc index 9975ff9d23f5b7..01da762ebddb26 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(); @@ -855,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) { @@ -1080,29 +1074,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 +1215,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_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 0d07eb5d907b57..e7282457a7052b 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, @@ -360,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 "," \ @@ -699,21 +374,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') 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); +}); 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' }); 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);