UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

430 lines (371 loc) 13 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/process/per_thread.js import * as __hoisted_internal_options__ from "nstdlib/lib/internal/options"; import * as __hoisted_internal_trace_events_async_hooks__ from "nstdlib/lib/internal/trace_events_async_hooks"; import { ErrnoException, codes as __codes__, } from "nstdlib/lib/internal/errors"; import { format as format } from "nstdlib/lib/internal/util/inspect"; import { validateArray, validateNumber, validateObject, } from "nstdlib/lib/internal/validators"; import { exitCodes as __exitCodes__ } from "nstdlib/stub/binding/errors"; import * as binding from "nstdlib/stub/binding/process_methods"; import * as __hoisted_internal_fs_utils__ from "nstdlib/lib/internal/fs/utils"; // This files contains process bootstrappers that can be // run when setting up each thread, including the main // thread and the worker threads. const { ERR_ASSERTION, ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, ERR_OUT_OF_RANGE, ERR_UNKNOWN_SIGNAL, } = __codes__; const constants = require("binding/constants").os.signals; let getValidatedPath; // We need to lazy load it because of the circular dependency. const kInternal = Symbol("internal properties"); function assert(x, msg) { if (!x) throw new ERR_ASSERTION(msg || "assertion error"); } const { kNoFailure } = __exitCodes__; // 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 = binding.hrtimeBuffer; // Use a BigUint64Array because this is actually a bit // faster than simply returning a BigInt from C++ in V8 7.1. const hrBigintValues = new BigUint64Array(binding.hrtimeBuffer.buffer, 0, 1); function hrtime(time) { binding.hrtime(); if (time !== undefined) { validateArray(time, "time"); if (time.length !== 2) { throw new ERR_OUT_OF_RANGE("time", 2, time.length); } const sec = hrValues[0] * 0x100000000 + hrValues[1] - time[0]; const nsec = hrValues[2] - time[1]; const needsBorrow = nsec < 0; return [needsBorrow ? sec - 1 : sec, needsBorrow ? nsec + 1e9 : nsec]; } return [hrValues[0] * 0x100000000 + hrValues[1], hrValues[2]]; } function hrtimeBigInt() { binding.hrtimeBigInt(); return hrBigintValues[0]; } function nop() {} // The execution of this function itself should not cause any side effects. function wrapProcessMethods(binding) { const { cpuUsage: _cpuUsage, memoryUsage: _memoryUsage, rss, resourceUsage: _resourceUsage, loadEnvFile: _loadEnvFile, } = binding; function _rawDebug(...args) { binding._rawDebug(ReflectApply(format, 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. function cpuUsage(prevValue) { // If a previous value was passed in, ensure it has the correct shape. if (prevValue) { if (!previousValueIsValid(prevValue.user)) { validateObject(prevValue, "prevValue"); validateNumber(prevValue.user, "prevValue.user"); throw new ERR_INVALID_ARG_VALUE.RangeError( "prevValue.user", prevValue.user, ); } if (!previousValueIsValid(prevValue.system)) { validateNumber(prevValue.system, "prevValue.system"); throw new ERR_INVALID_ARG_VALUE.RangeError( "prevValue.system", prevValue.system, ); } } // Call the native function to get the current values. _cpuUsage(cpuValues); // If a previous value was passed in, return diff of current from previous. if (prevValue) { return { user: cpuValues[0] - prevValue.user, system: cpuValues[1] - prevValue.system, }; } // If no previous value passed in, return current value. return { 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. function previousValueIsValid(num) { return ( typeof num === "number" && num <= Number.MAX_SAFE_INTEGER && num >= 0 ); } const memValues = new Float64Array(5); function memoryUsage() { _memoryUsage(memValues); return { rss: memValues[0], heapTotal: memValues[1], heapUsed: memValues[2], external: memValues[3], arrayBuffers: memValues[4], }; } memoryUsage.rss = rss; function exit(code) { if (arguments.length !== 0) { process.exitCode = code; } if (!process._exiting) { process._exiting = true; process.emit("exit", process.exitCode || kNoFailure); } // FIXME(joyeecheung): This is an undocumented API that gets monkey-patched // in the user land. Either document it, or deprecate it in favor of a // better public alternative. process.reallyExit(process.exitCode || kNoFailure); // If this is a worker, v8::Isolate::TerminateExecution() is called above. // That function spoofs the stack pointer to cause the stack guard // check to throw the termination exception. Because v8 performs // stack guard check upon every function call, we give it a chance. // // Without this, user code after `process.exit()` would take effect. // test/parallel/test-worker-voluntarily-exit-followed-by-addition.js // test/parallel/test-worker-voluntarily-exit-followed-by-throw.js nop(); } function kill(pid, sig) { let err; // eslint-disable-next-line eqeqeq if (pid != (pid | 0)) { throw new ERR_INVALID_ARG_TYPE("pid", "number", pid); } // 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"; if (constants[sig]) { err = process._kill(pid, constants[sig]); } else { throw new ERR_UNKNOWN_SIGNAL(sig); } } if (err) throw new ErrnoException(err, "kill"); return true; } const resourceValues = new Float64Array(16); function resourceUsage() { _resourceUsage(resourceValues); return { userCPUTime: resourceValues[0], systemCPUTime: resourceValues[1], maxRSS: resourceValues[2], sharedMemorySize: resourceValues[3], unsharedDataSize: resourceValues[4], unsharedStackSize: resourceValues[5], minorPageFault: resourceValues[6], majorPageFault: resourceValues[7], swappedOut: resourceValues[8], fsRead: resourceValues[9], fsWrite: resourceValues[10], ipcSent: resourceValues[11], ipcReceived: resourceValues[12], signalsCount: resourceValues[13], voluntaryContextSwitches: resourceValues[14], involuntaryContextSwitches: resourceValues[15], }; } /** * Loads the `.env` file to process.env. * @param {string | URL | Buffer | undefined} path */ function loadEnvFile(path = undefined) { // Provide optional value so that `loadEnvFile.length` returns 0 if (path != null) { getValidatedPath ??= __hoisted_internal_fs_utils__.getValidatedPath; path = getValidatedPath(path); _loadEnvFile(path); } else { _loadEnvFile(); } } return { _rawDebug, cpuUsage, resourceUsage, memoryUsage, kill, exit, loadEnvFile, }; } const replaceUnderscoresRegex = /_/g; const leadingDashesRegex = /^--?/; const trailingValuesRegex = /=.*$/; // This builds the initial process.allowedNodeEnvironmentFlags // from data in the config binding. function buildAllowedFlags() { const { envSettings: { kAllowedInEnvvar }, types: { kBoolean }, } = require("binding/options"); const { getCLIOptionsInfo } = __hoisted_internal_options__; const { options, aliases } = getCLIOptionsInfo(); const allowedNodeEnvironmentFlags = []; for (const { 0: name, 1: info } of options) { if (info.envVarSettings === kAllowedInEnvvar) { Array.prototype.push.call(allowedNodeEnvironmentFlags, name); if (info.type === kBoolean) { const negatedName = `--no-${name.slice(2)}`; Array.prototype.push.call(allowedNodeEnvironmentFlags, negatedName); } } } function isAccepted(to) { if (!String.prototype.startsWith.call(to, "-") || to === "--") return true; const recursiveExpansion = aliases.get(to); if (recursiveExpansion) { if (recursiveExpansion[0] === to) Array.prototype.splice.call(recursiveExpansion, 0, 1); return Array.prototype.every.call(recursiveExpansion, isAccepted); } return options.get(to).envVarSettings === kAllowedInEnvvar; } for (const { 0: from, 1: expansion } of aliases) { if (Array.prototype.every.call(expansion, isAccepted)) { let canonical = from; if (String.prototype.endsWith.call(canonical, "=")) canonical = String.prototype.slice.call( canonical, 0, canonical.length - 1, ); if (String.prototype.endsWith.call(canonical, " <arg>")) canonical = String.prototype.slice.call( canonical, 0, canonical.length - 4, ); Array.prototype.push.call(allowedNodeEnvironmentFlags, canonical); } } const trimLeadingDashes = (flag) => String.prototype.replace.call(flag, leadingDashesRegex, ""); // Save these for comparison against flags provided to // process.allowedNodeEnvironmentFlags.has() which lack leading dashes. const nodeFlags = Array.prototype.map.call( allowedNodeEnvironmentFlags, trimLeadingDashes, ); class NodeEnvironmentFlagsSet extends Set { constructor(array) { super(); this[kInternal] = { array }; } add() { // No-op, `Set` API compatible return this; } delete() { // No-op, `Set` API compatible return false; } clear() { // No-op, `Set` API compatible } has(key) { // This will return `true` based on various possible // permutations of a flag, including present/missing leading // dash(es) and/or underscores-for-dashes. // Strips any values after `=`, inclusive. // TODO(addaleax): It might be more flexible to run the option parser // on a dummy option set and see whether it rejects the argument or // not. if (typeof key === "string") { key = String.prototype.replace.call(key, replaceUnderscoresRegex, "-"); if (RegExp.prototype.exec.call(leadingDashesRegex, key) !== null) { key = String.prototype.replace.call(key, trailingValuesRegex, ""); return Array.prototype.includes.call(this[kInternal].array, key); } return Array.prototype.includes.call(nodeFlags, key); } return false; } entries() { this[kInternal].set ??= new Set( new (Array.prototype[Symbol.iterator]())(this[kInternal].array), ); return Set.prototype.entries.call(this[kInternal].set); } forEach(callback, thisArg = undefined) { Array.prototype.forEach.call(this[kInternal].array, (v) => ReflectApply(callback, thisArg, [v, v, this]), ); } get size() { return this[kInternal].array.length; } values() { this[kInternal].set ??= new Set( new (Array.prototype[Symbol.iterator]())(this[kInternal].array), ); return Set.prototype.values.call(this[kInternal].set); } } const flagSetValues = NodeEnvironmentFlagsSet.prototype.values; Object.defineProperty(NodeEnvironmentFlagsSet.prototype, Symbol.iterator, { __proto__: null, value: flagSetValues, }); Object.defineProperty(NodeEnvironmentFlagsSet.prototype, "keys", { __proto__: null, value: flagSetValues, }); Object.freeze(NodeEnvironmentFlagsSet.prototype.constructor); Object.freeze(NodeEnvironmentFlagsSet.prototype); return Object.freeze( new NodeEnvironmentFlagsSet(allowedNodeEnvironmentFlags), ); } // Lazy load internal/trace_events_async_hooks only if the async_hooks // trace event category is enabled. let traceEventsAsyncHook; // Dynamically enable/disable the traceEventsAsyncHook function toggleTraceCategoryState(asyncHooksEnabled) { if (asyncHooksEnabled) { if (!traceEventsAsyncHook) { traceEventsAsyncHook = __hoisted_internal_trace_events_async_hooks__.createHook(); } traceEventsAsyncHook.enable(); } else if (traceEventsAsyncHook) { traceEventsAsyncHook.disable(); } } const { arch, platform, version } = process; export { toggleTraceCategoryState }; export { assert }; export { buildAllowedFlags }; export { wrapProcessMethods }; export { hrtime }; export { hrtimeBigInt }; export { arch }; export { platform }; export { version };