'use strict'; // This files contains process bootstrappers that can be // run when setting up each thread, including the main // thread and the worker threads. const { errnoException, codes: { ERR_ASSERTION, ERR_CPU_USAGE, ERR_INVALID_ARG_TYPE, ERR_INVALID_OPT_VALUE, ERR_OUT_OF_RANGE, ERR_UNKNOWN_SIGNAL } } = require('internal/errors'); const util = require('util'); const constants = internalBinding('constants').os.signals; function assert(x, msg) { if (!x) throw new ERR_ASSERTION(msg || 'assertion error'); } // The execution of this function itself should not cause any side effects. function wrapProcessMethods(binding) { 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. function cpuUsage(prevValue) { // If a previous value was passed in, ensure it has the correct shape. if (prevValue) { if (!previousValueIsValid(prevValue.user)) { if (typeof prevValue !== 'object') throw new ERR_INVALID_ARG_TYPE('prevValue', 'object', prevValue); if (typeof prevValue.user !== 'number') { throw new ERR_INVALID_ARG_TYPE('prevValue.user', 'number', prevValue.user); } throw new ERR_INVALID_OPT_VALUE.RangeError('prevValue.user', prevValue.user); } if (!previousValueIsValid(prevValue.system)) { if (typeof prevValue.system !== 'number') { throw new ERR_INVALID_ARG_TYPE('prevValue.system', 'number', prevValue.system); } throw new ERR_INVALID_OPT_VALUE.RangeError('prevValue.system', prevValue.system); } } // Call the native function to get the current values. const errmsg = _cpuUsage(cpuValues); if (errmsg) { throw new ERR_CPU_USAGE(errmsg); } // 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; } // 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); function hrtime(time) { _hrtime(hrValues); if (time !== undefined) { if (!Array.isArray(time)) { throw new ERR_INVALID_ARG_TYPE('time', 'Array', 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] ]; } // 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); function hrtimeBigInt() { _hrtimeBigInt(hrBigintValues); return hrBigintValues[0]; } const memValues = new Float64Array(4); function memoryUsage() { _memoryUsage(memValues); return { rss: memValues[0], heapTotal: memValues[1], heapUsed: memValues[2], external: memValues[3] }; } function exit(code) { if (code || code === 0) process.exitCode = code; if (!process._exiting) { process._exiting = true; process.emit('exit', process.exitCode || 0); } binding.reallyExit(process.exitCode || 0); } function kill(pid, sig) { var err; if (process.env.NODE_V8_COVERAGE) { const { writeCoverage } = require('internal/coverage-gen/with_profiler'); writeCoverage(); } // 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 errnoException(err, 'kill'); return true; } return { _rawDebug, hrtime, hrtimeBigInt, cpuUsage, memoryUsage, kill, exit }; } const replaceUnderscoresRegex = /_/g; const leadingDashesRegex = /^--?/; const trailingValuesRegex = /=.*$/; // Save references so user code does not interfere const replace = Function.call.bind(String.prototype.replace); const has = Function.call.bind(Set.prototype.has); const test = Function.call.bind(RegExp.prototype.test); // This builds the initial process.allowedNodeEnvironmentFlags // from data in the config binding. function buildAllowedFlags() { const { envSettings: { kAllowedInEnvironment } } = internalBinding('options'); const { options, aliases } = require('internal/options'); const allowedNodeEnvironmentFlags = []; for (const [name, info] of options) { if (info.envVarSettings === kAllowedInEnvironment) { allowedNodeEnvironmentFlags.push(name); } } for (const [ from, expansion ] of aliases) { let isAccepted = true; for (const to of expansion) { if (!to.startsWith('-') || to === '--') continue; const recursiveExpansion = aliases.get(to); if (recursiveExpansion) { if (recursiveExpansion[0] === to) recursiveExpansion.splice(0, 1); expansion.push(...recursiveExpansion); continue; } isAccepted = options.get(to).envVarSettings === kAllowedInEnvironment; if (!isAccepted) break; } if (isAccepted) { let canonical = from; if (canonical.endsWith('=')) canonical = canonical.substr(0, canonical.length - 1); if (canonical.endsWith(' ')) canonical = canonical.substr(0, canonical.length - 4); allowedNodeEnvironmentFlags.push(canonical); } } const trimLeadingDashes = (flag) => replace(flag, leadingDashesRegex, ''); // Save these for comparison against flags provided to // process.allowedNodeEnvironmentFlags.has() which lack leading dashes. // Avoid interference w/ user code by flattening `Set.prototype` into // each object. const nodeFlags = Object.defineProperties( new Set(allowedNodeEnvironmentFlags.map(trimLeadingDashes)), Object.getOwnPropertyDescriptors(Set.prototype) ); class NodeEnvironmentFlagsSet extends Set { constructor(...args) { super(...args); // the super constructor consumes `add`, but // disallow any future adds. this.add = () => this; } delete() { // noop, `Set` API compatible return false; } clear() { // noop } 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 = replace(key, replaceUnderscoresRegex, '-'); if (test(leadingDashesRegex, key)) { key = replace(key, trailingValuesRegex, ''); return has(this, key); } return has(nodeFlags, key); } return false; } } Object.freeze(NodeEnvironmentFlagsSet.prototype.constructor); Object.freeze(NodeEnvironmentFlagsSet.prototype); return Object.freeze(new NodeEnvironmentFlagsSet( allowedNodeEnvironmentFlags )); } module.exports = { assert, buildAllowedFlags, wrapProcessMethods };