mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
94be10ac32
PR-URL: https://github.com/nodejs/node/pull/55740 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Chemi Atlow <chemi@atlow.co.il> Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
492 lines
15 KiB
JavaScript
492 lines
15 KiB
JavaScript
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
// persons to whom the Software is furnished to do so, subject to the
|
|
// following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
'use strict';
|
|
|
|
const {
|
|
ArrayIsArray,
|
|
ArrayPrototypePop,
|
|
ArrayPrototypePush,
|
|
Error,
|
|
ErrorCaptureStackTrace,
|
|
FunctionPrototypeBind,
|
|
NumberIsSafeInteger,
|
|
ObjectDefineProperties,
|
|
ObjectDefineProperty,
|
|
ObjectGetOwnPropertyDescriptors,
|
|
ObjectKeys,
|
|
ObjectSetPrototypeOf,
|
|
ObjectValues,
|
|
ReflectApply,
|
|
StringPrototypeToWellFormed,
|
|
} = primordials;
|
|
|
|
const {
|
|
ErrnoException,
|
|
ExceptionWithHostPort,
|
|
codes: {
|
|
ERR_FALSY_VALUE_REJECTION,
|
|
ERR_INVALID_ARG_TYPE,
|
|
ERR_OUT_OF_RANGE,
|
|
},
|
|
isErrorStackTraceLimitWritable,
|
|
} = require('internal/errors');
|
|
const {
|
|
format,
|
|
formatWithOptions,
|
|
inspect,
|
|
stripVTControlCharacters,
|
|
} = require('internal/util/inspect');
|
|
const { debuglog } = require('internal/util/debuglog');
|
|
const {
|
|
validateBoolean,
|
|
validateFunction,
|
|
validateNumber,
|
|
validateString,
|
|
validateOneOf,
|
|
validateObject,
|
|
} = require('internal/validators');
|
|
const {
|
|
isReadableStream,
|
|
isWritableStream,
|
|
isNodeStream,
|
|
} = require('internal/streams/utils');
|
|
const types = require('internal/util/types');
|
|
|
|
let utilColors;
|
|
function lazyUtilColors() {
|
|
utilColors ??= require('internal/util/colors');
|
|
return utilColors;
|
|
}
|
|
const { getOptionValue } = require('internal/options');
|
|
|
|
const binding = internalBinding('util');
|
|
|
|
const {
|
|
deprecate,
|
|
getLazy,
|
|
getSystemErrorMap,
|
|
getSystemErrorName: internalErrorName,
|
|
getSystemErrorMessage: internalErrorMessage,
|
|
promisify,
|
|
defineLazyProperties,
|
|
} = require('internal/util');
|
|
|
|
let abortController;
|
|
|
|
function lazyAbortController() {
|
|
abortController ??= require('internal/abort_controller');
|
|
return abortController;
|
|
}
|
|
|
|
let internalDeepEqual;
|
|
|
|
/**
|
|
* @param {string} code
|
|
* @returns {string}
|
|
*/
|
|
function escapeStyleCode(code) {
|
|
return `\u001b[${code}m`;
|
|
}
|
|
|
|
/**
|
|
* @param {string | string[]} format
|
|
* @param {string} text
|
|
* @param {object} [options={}]
|
|
* @param {boolean} [options.validateStream=true] - Whether to validate the stream.
|
|
* @param {Stream} [options.stream=process.stdout] - The stream used for validation.
|
|
* @returns {string}
|
|
*/
|
|
function styleText(format, text, { validateStream = true, stream = process.stdout } = {}) {
|
|
validateString(text, 'text');
|
|
validateBoolean(validateStream, 'options.validateStream');
|
|
|
|
if (validateStream) {
|
|
if (
|
|
!isReadableStream(stream) &&
|
|
!isWritableStream(stream) &&
|
|
!isNodeStream(stream)
|
|
) {
|
|
throw new ERR_INVALID_ARG_TYPE('stream', ['ReadableStream', 'WritableStream', 'Stream'], stream);
|
|
}
|
|
}
|
|
|
|
if (ArrayIsArray(format)) {
|
|
let left = '';
|
|
let right = '';
|
|
for (const key of format) {
|
|
const formatCodes = inspect.colors[key];
|
|
if (formatCodes == null) {
|
|
validateOneOf(key, 'format', ObjectKeys(inspect.colors));
|
|
}
|
|
left += escapeStyleCode(formatCodes[0]);
|
|
right = `${escapeStyleCode(formatCodes[1])}${right}`;
|
|
}
|
|
|
|
return `${left}${text}${right}`;
|
|
}
|
|
|
|
const formatCodes = inspect.colors[format];
|
|
if (formatCodes == null) {
|
|
validateOneOf(format, 'format', ObjectKeys(inspect.colors));
|
|
}
|
|
|
|
// Check colorize only after validating arg type and value
|
|
if (
|
|
validateStream &&
|
|
(
|
|
!stream ||
|
|
!lazyUtilColors().shouldColorize(stream)
|
|
)
|
|
) {
|
|
return text;
|
|
}
|
|
|
|
return `${escapeStyleCode(formatCodes[0])}${text}${escapeStyleCode(formatCodes[1])}`;
|
|
}
|
|
|
|
/**
|
|
* Inherit the prototype methods from one constructor into another.
|
|
*
|
|
* The Function.prototype.inherits from lang.js rewritten as a standalone
|
|
* function (not on Function.prototype). NOTE: If this file is to be loaded
|
|
* during bootstrapping this function needs to be rewritten using some native
|
|
* functions as prototype setup using normal JavaScript does not work as
|
|
* expected during bootstrapping (see mirror.js in r114903).
|
|
* @param {Function} ctor Constructor function which needs to inherit the
|
|
* prototype.
|
|
* @param {Function} superCtor Constructor function to inherit prototype from.
|
|
* @throws {TypeError} Will error if either constructor is null, or if
|
|
* the super constructor lacks a prototype.
|
|
*/
|
|
function inherits(ctor, superCtor) {
|
|
|
|
if (ctor === undefined || ctor === null)
|
|
throw new ERR_INVALID_ARG_TYPE('ctor', 'Function', ctor);
|
|
|
|
if (superCtor === undefined || superCtor === null)
|
|
throw new ERR_INVALID_ARG_TYPE('superCtor', 'Function', superCtor);
|
|
|
|
if (superCtor.prototype === undefined) {
|
|
throw new ERR_INVALID_ARG_TYPE('superCtor.prototype',
|
|
'Object', superCtor.prototype);
|
|
}
|
|
ObjectDefineProperty(ctor, 'super_', {
|
|
__proto__: null,
|
|
value: superCtor,
|
|
writable: true,
|
|
configurable: true,
|
|
});
|
|
ObjectSetPrototypeOf(ctor.prototype, superCtor.prototype);
|
|
}
|
|
|
|
/**
|
|
* @deprecated since v6.0.0
|
|
* @template T
|
|
* @template S
|
|
* @param {T} target
|
|
* @param {S} source
|
|
* @returns {S extends null ? T : (T & S)}
|
|
*/
|
|
function _extend(target, source) {
|
|
// Don't do anything if source isn't an object
|
|
if (source === null || typeof source !== 'object') return target;
|
|
|
|
const keys = ObjectKeys(source);
|
|
let i = keys.length;
|
|
while (i--) {
|
|
target[keys[i]] = source[keys[i]];
|
|
}
|
|
return target;
|
|
}
|
|
|
|
const callbackifyOnRejected = (reason, cb) => {
|
|
// `!reason` guard inspired by bluebird (Ref: https://goo.gl/t5IS6M).
|
|
// Because `null` is a special error value in callbacks which means "no error
|
|
// occurred", we error-wrap so the callback consumer can distinguish between
|
|
// "the promise rejected with null" or "the promise fulfilled with undefined".
|
|
if (!reason) {
|
|
reason = new ERR_FALSY_VALUE_REJECTION.HideStackFramesError(reason);
|
|
ErrorCaptureStackTrace(reason, callbackifyOnRejected);
|
|
}
|
|
return cb(reason);
|
|
};
|
|
|
|
/**
|
|
* @template {(...args: any[]) => Promise<any>} T
|
|
* @param {T} original
|
|
* @returns {T extends (...args: infer TArgs) => Promise<infer TReturn> ?
|
|
* ((...params: [...TArgs, ((err: Error, ret: TReturn) => any)]) => void) :
|
|
* never
|
|
* }
|
|
*/
|
|
function callbackify(original) {
|
|
validateFunction(original, 'original');
|
|
|
|
// We DO NOT return the promise as it gives the user a false sense that
|
|
// the promise is actually somehow related to the callback's execution
|
|
// and that the callback throwing will reject the promise.
|
|
function callbackified(...args) {
|
|
const maybeCb = ArrayPrototypePop(args);
|
|
validateFunction(maybeCb, 'last argument');
|
|
const cb = FunctionPrototypeBind(maybeCb, this);
|
|
// In true node style we process the callback on `nextTick` with all the
|
|
// implications (stack, `uncaughtException`, `async_hooks`)
|
|
ReflectApply(original, this, args)
|
|
.then((ret) => process.nextTick(cb, null, ret),
|
|
(rej) => process.nextTick(callbackifyOnRejected, rej, cb));
|
|
}
|
|
|
|
const descriptors = ObjectGetOwnPropertyDescriptors(original);
|
|
// It is possible to manipulate a functions `length` or `name` property. This
|
|
// guards against the manipulation.
|
|
if (typeof descriptors.length.value === 'number') {
|
|
descriptors.length.value++;
|
|
}
|
|
if (typeof descriptors.name.value === 'string') {
|
|
descriptors.name.value += 'Callbackified';
|
|
}
|
|
const propertiesValues = ObjectValues(descriptors);
|
|
for (let i = 0; i < propertiesValues.length; i++) {
|
|
// We want to use null-prototype objects to not rely on globally mutable
|
|
// %Object.prototype%.
|
|
ObjectSetPrototypeOf(propertiesValues[i], null);
|
|
}
|
|
ObjectDefineProperties(callbackified, descriptors);
|
|
return callbackified;
|
|
}
|
|
|
|
/**
|
|
* @param {number} err
|
|
* @returns {string}
|
|
*/
|
|
function getSystemErrorMessage(err) {
|
|
validateNumber(err, 'err');
|
|
if (err >= 0 || !NumberIsSafeInteger(err)) {
|
|
throw new ERR_OUT_OF_RANGE('err', 'a negative integer', err);
|
|
}
|
|
return internalErrorMessage(err);
|
|
}
|
|
|
|
/**
|
|
* @param {number} err
|
|
* @returns {string}
|
|
*/
|
|
function getSystemErrorName(err) {
|
|
validateNumber(err, 'err');
|
|
if (err >= 0 || !NumberIsSafeInteger(err)) {
|
|
throw new ERR_OUT_OF_RANGE('err', 'a negative integer', err);
|
|
}
|
|
return internalErrorName(err);
|
|
}
|
|
|
|
function _errnoException(...args) {
|
|
if (isErrorStackTraceLimitWritable()) {
|
|
const limit = Error.stackTraceLimit;
|
|
Error.stackTraceLimit = 0;
|
|
const e = new ErrnoException(...args);
|
|
Error.stackTraceLimit = limit;
|
|
ErrorCaptureStackTrace(e, _exceptionWithHostPort);
|
|
return e;
|
|
}
|
|
return new ErrnoException(...args);
|
|
}
|
|
|
|
function _exceptionWithHostPort(...args) {
|
|
if (isErrorStackTraceLimitWritable()) {
|
|
const limit = Error.stackTraceLimit;
|
|
Error.stackTraceLimit = 0;
|
|
const e = new ExceptionWithHostPort(...args);
|
|
Error.stackTraceLimit = limit;
|
|
ErrorCaptureStackTrace(e, _exceptionWithHostPort);
|
|
return e;
|
|
}
|
|
return new ExceptionWithHostPort(...args);
|
|
}
|
|
|
|
/**
|
|
* Parses the content of a `.env` file.
|
|
* @param {string} content
|
|
* @returns {Record<string, string>}
|
|
*/
|
|
function parseEnv(content) {
|
|
validateString(content, 'content');
|
|
return binding.parseEnv(content);
|
|
}
|
|
|
|
const lazySourceMap = getLazy(() => require('internal/source_map/source_map_cache'));
|
|
|
|
/**
|
|
* @typedef {object} CallSite // The call site
|
|
* @property {string} scriptName // The name of the resource that contains the
|
|
* script for the function for this StackFrame
|
|
* @property {string} functionName // The name of the function associated with this stack frame
|
|
* @property {number} lineNumber // The number, 1-based, of the line for the associate function call
|
|
* @property {number} columnNumber // The 1-based column offset on the line for the associated function call
|
|
*/
|
|
|
|
/**
|
|
* @param {CallSite} callSite // The call site object to reconstruct from source map
|
|
* @returns {CallSite | undefined} // The reconstructed call site object
|
|
*/
|
|
function reconstructCallSite(callSite) {
|
|
const { scriptName, lineNumber, column } = callSite;
|
|
const sourceMap = lazySourceMap().findSourceMap(scriptName);
|
|
if (!sourceMap) return;
|
|
const entry = sourceMap.findEntry(lineNumber - 1, column - 1);
|
|
if (!entry?.originalSource) return;
|
|
return {
|
|
__proto__: null,
|
|
// If the name is not found, it is an empty string to match the behavior of `util.getCallSite()`
|
|
functionName: entry.name ?? '',
|
|
scriptName: entry.originalSource,
|
|
lineNumber: entry.originalLine + 1,
|
|
column: entry.originalColumn + 1,
|
|
};
|
|
}
|
|
|
|
/**
|
|
*
|
|
* The call site array to map
|
|
* @param {CallSite[]} callSites
|
|
* Array of objects with the reconstructed call site
|
|
* @returns {CallSite[]}
|
|
*/
|
|
function mapCallSite(callSites) {
|
|
const result = [];
|
|
for (let i = 0; i < callSites.length; ++i) {
|
|
const callSite = callSites[i];
|
|
const found = reconstructCallSite(callSite);
|
|
ArrayPrototypePush(result, found ?? callSite);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @typedef {object} CallSiteOptions // The call site options
|
|
* @property {boolean} sourceMap // Enable source map support
|
|
*/
|
|
|
|
/**
|
|
* Returns the callSite
|
|
* @param {number} frameCount
|
|
* @param {CallSiteOptions} options
|
|
* @returns {CallSite[]}
|
|
*/
|
|
function getCallSites(frameCount = 10, options) {
|
|
// If options is not provided check if frameCount is an object
|
|
if (options === undefined) {
|
|
if (typeof frameCount === 'object') {
|
|
// If frameCount is an object, it is the options object
|
|
options = frameCount;
|
|
validateObject(options, 'options');
|
|
validateBoolean(options.sourceMap, 'options.sourceMap');
|
|
frameCount = 10;
|
|
} else {
|
|
// If options is not provided, set it to an empty object
|
|
options = {};
|
|
};
|
|
} else {
|
|
// If options is provided, validate it
|
|
validateObject(options, 'options');
|
|
validateBoolean(options.sourceMap, 'options.sourceMap');
|
|
}
|
|
|
|
// Using kDefaultMaxCallStackSizeToCapture as reference
|
|
validateNumber(frameCount, 'frameCount', 1, 200);
|
|
// If options.sourceMaps is true or if sourceMaps are enabled but the option.sourceMaps is not set explictly to false
|
|
if (options.sourceMap === true || (getOptionValue('--enable-source-maps') && options.sourceMap !== false)) {
|
|
return mapCallSite(binding.getCallSites(frameCount));
|
|
}
|
|
return binding.getCallSites(frameCount);
|
|
};
|
|
|
|
// Keep the `exports =` so that various functions can still be monkeypatched
|
|
module.exports = {
|
|
_errnoException,
|
|
_exceptionWithHostPort,
|
|
_extend: deprecate(_extend,
|
|
'The `util._extend` API is deprecated. Please use Object.assign() instead.',
|
|
'DEP0060'),
|
|
callbackify,
|
|
debug: debuglog,
|
|
debuglog,
|
|
deprecate,
|
|
format,
|
|
styleText,
|
|
formatWithOptions,
|
|
// Deprecated getCallSite.
|
|
// This API can be removed in next semver-minor release.
|
|
getCallSite: deprecate(getCallSites,
|
|
'The `util.getCallSite` API has been renamed to `util.getCallSites()`.',
|
|
'ExperimentalWarning'),
|
|
getCallSites,
|
|
getSystemErrorMap,
|
|
getSystemErrorName,
|
|
getSystemErrorMessage,
|
|
inherits,
|
|
inspect,
|
|
isArray: deprecate(ArrayIsArray,
|
|
'The `util.isArray` API is deprecated. Please use `Array.isArray()` instead.',
|
|
'DEP0044'),
|
|
isDeepStrictEqual(a, b) {
|
|
if (internalDeepEqual === undefined) {
|
|
internalDeepEqual = require('internal/util/comparisons')
|
|
.isDeepStrictEqual;
|
|
}
|
|
return internalDeepEqual(a, b);
|
|
},
|
|
promisify,
|
|
stripVTControlCharacters,
|
|
toUSVString(input) {
|
|
return StringPrototypeToWellFormed(`${input}`);
|
|
},
|
|
get transferableAbortSignal() {
|
|
return lazyAbortController().transferableAbortSignal;
|
|
},
|
|
get transferableAbortController() {
|
|
return lazyAbortController().transferableAbortController;
|
|
},
|
|
get aborted() {
|
|
return lazyAbortController().aborted;
|
|
},
|
|
types,
|
|
parseEnv,
|
|
};
|
|
|
|
defineLazyProperties(
|
|
module.exports,
|
|
'internal/util/parse_args/parse_args',
|
|
['parseArgs'],
|
|
);
|
|
|
|
defineLazyProperties(
|
|
module.exports,
|
|
'internal/encoding',
|
|
['TextDecoder', 'TextEncoder'],
|
|
);
|
|
|
|
defineLazyProperties(
|
|
module.exports,
|
|
'internal/mime',
|
|
['MIMEType', 'MIMEParams'],
|
|
);
|