node/lib/util.js

475 lines
14 KiB
JavaScript
Raw Normal View History

// 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,
ArrayPrototypeJoin,
ArrayPrototypePop,
Date,
DatePrototypeGetDate,
DatePrototypeGetHours,
DatePrototypeGetMinutes,
DatePrototypeGetMonth,
DatePrototypeGetSeconds,
Error,
ErrorCaptureStackTrace,
FunctionPrototypeBind,
NumberIsSafeInteger,
ObjectDefineProperties,
ObjectDefineProperty,
ObjectGetOwnPropertyDescriptors,
ObjectKeys,
ObjectPrototypeToString,
ObjectSetPrototypeOf,
ObjectValues,
ReflectApply,
StringPrototypePadStart,
} = primordials;
const {
codes: {
ERR_FALSY_VALUE_REJECTION,
ERR_INVALID_ARG_TYPE,
ERR_OUT_OF_RANGE,
},
isErrorStackTraceLimitWritable,
ErrnoException,
ExceptionWithHostPort,
} = require('internal/errors');
const {
format,
formatWithOptions,
inspect,
stripVTControlCharacters,
} = require('internal/util/inspect');
const { debuglog } = require('internal/util/debuglog');
const {
validateFunction,
validateNumber,
} = require('internal/validators');
const { isBuffer } = require('buffer').Buffer;
const types = require('internal/util/types');
const {
deprecate,
getSystemErrorMap,
getSystemErrorName: internalErrorName,
promisify,
toUSVString,
defineLazyProperties,
} = require('internal/util');
let abortController;
function lazyAbortController() {
abortController ??= require('internal/abort_controller');
return abortController;
}
let internalDeepEqual;
/**
* @deprecated since v4.0.0
* @param {any} arg
* @returns {arg is boolean}
*/
function isBoolean(arg) {
return typeof arg === 'boolean';
}
/**
* @deprecated since v4.0.0
* @param {any} arg
* @returns {arg is null}
*/
function isNull(arg) {
return arg === null;
}
/**
* @deprecated since v4.0.0
* @param {any} arg
* @returns {arg is (null | undefined)}
*/
function isNullOrUndefined(arg) {
return arg === null || arg === undefined;
}
/**
* @deprecated since v4.0.0
* @param {any} arg
* @returns {arg is number}
*/
function isNumber(arg) {
return typeof arg === 'number';
}
/**
* @param {any} arg
* @returns {arg is string}
*/
function isString(arg) {
return typeof arg === 'string';
}
/**
* @deprecated since v4.0.0
* @param {any} arg
* @returns {arg is symbol}
*/
function isSymbol(arg) {
return typeof arg === 'symbol';
}
/**
* @deprecated since v4.0.0
* @param {any} arg
* @returns {arg is undefined}
*/
function isUndefined(arg) {
return arg === undefined;
}
/**
* @deprecated since v4.0.0
* @param {any} arg
* @returns {a is NonNullable<object>}
*/
function isObject(arg) {
return arg !== null && typeof arg === 'object';
}
/**
* @deprecated since v4.0.0
* @param {any} e
* @returns {arg is Error}
*/
function isError(e) {
return ObjectPrototypeToString(e) === '[object Error]' || e instanceof Error;
}
/**
* @deprecated since v4.0.0
* @param {any} arg
* @returns {arg is Function}
*/
function isFunction(arg) {
return typeof arg === 'function';
}
/**
* @deprecated since v4.0.0
* @param {any} arg
* @returns {arg is (boolean | null | number | string | symbol | undefined)}
*/
2013-08-02 19:52:34 +00:00
function isPrimitive(arg) {
return arg === null ||
(typeof arg !== 'object' && typeof arg !== 'function');
2013-08-02 19:52:34 +00:00
}
/**
* @param {number} n
* @returns {string}
*/
2010-12-02 04:59:06 +00:00
function pad(n) {
return StringPrototypePadStart(n.toString(), 2, '0');
}
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
'Oct', 'Nov', 'Dec'];
/**
* @returns {string} 26 Feb 16:19:34
*/
2010-12-02 04:59:06 +00:00
function timestamp() {
const d = new Date();
const t = ArrayPrototypeJoin([
pad(DatePrototypeGetHours(d)),
pad(DatePrototypeGetMinutes(d)),
pad(DatePrototypeGetSeconds(d)),
], ':');
return `${DatePrototypeGetDate(d)} ${months[DatePrototypeGetMonth(d)]} ${t}`;
}
let console;
/**
* Log is just a thin wrapper to console.log that prepends a timestamp
* @deprecated since v6.0.0
* @type {(...args: any[]) => void}
*/
function log(...args) {
if (!console) {
console = require('internal/console/global');
}
console.log('%s - %s', timestamp(), format(...args));
}
/**
* 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
2012-02-18 05:28:13 +00:00
* 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
2010-12-02 04:59:06 +00:00
* 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 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);
}
// 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,
formatWithOptions,
getSystemErrorMap,
getSystemErrorName,
inherits,
inspect,
isArray: deprecate(ArrayIsArray,
'The `util.isArray` API is deprecated. Please use `Array.isArray()` instead.',
'DEP0044'),
isBoolean: deprecate(isBoolean,
'The `util.isBoolean` API is deprecated. Please use `typeof arg === "boolean"` instead.',
'DEP0045'),
isBuffer: deprecate(isBuffer,
'The `util.isBuffer` API is deprecated. Please use `Buffer.isBuffer()` instead.',
'DEP0046'),
isDeepStrictEqual(a, b) {
if (internalDeepEqual === undefined) {
internalDeepEqual = require('internal/util/comparisons')
.isDeepStrictEqual;
}
return internalDeepEqual(a, b);
},
isNull: deprecate(isNull,
'The `util.isNull` API is deprecated. Please use `arg === null` instead.',
'DEP0050'),
isNullOrUndefined: deprecate(isNullOrUndefined,
'The `util.isNullOrUndefined` API is deprecated. ' +
'Please use `arg === null || arg === undefined` instead.',
'DEP0051'),
isNumber: deprecate(isNumber,
'The `util.isNumber` API is deprecated. Please use `typeof arg === "number"` instead.',
'DEP0052'),
isString: deprecate(isString,
'The `util.isString` API is deprecated. Please use `typeof arg === "string"` instead.',
'DEP0056'),
isSymbol: deprecate(isSymbol,
'The `util.isSymbol` API is deprecated. Please use `arg === "symbol"` instead.',
'DEP0057'),
isUndefined: deprecate(isUndefined,
'The `util.isUndefined` API is deprecated. Please use `arg === undefined` instead.',
'DEP0058'),
isRegExp: deprecate(types.isRegExp,
'The `util.isRegExp` API is deprecated. Please use `arg instanceof RegExp` instead.',
'DEP0055'),
isObject: deprecate(isObject,
'The `util.isObject` API is deprecated. Please use `typeof arg === "object"` instead.',
'DEP0053'),
isDate: deprecate(types.isDate,
'The `util.isDate API is deprecated. Please use `arg instanceof Error` instead.',
'DEP0047'),
isError: deprecate(isError,
'The `util.isError` API is deprecated. ' +
'Please use `ObjectPrototypeToString(e) === "[object Error]" ' +
'|| e instanceof Error` instead.',
'DEP0048'),
isFunction: deprecate(isFunction, 'The `util.isFunction API is deprecated.', 'DEP0049'),
isPrimitive: deprecate(isPrimitive, 'The `util.isPrimitive API is deprecated.', 'DEP0054'),
log: deprecate(log, 'The `util.log API is deprecated.', 'DEP0059'),
promisify,
stripVTControlCharacters,
toUSVString,
get transferableAbortSignal() {
return lazyAbortController().transferableAbortSignal;
},
get transferableAbortController() {
return lazyAbortController().transferableAbortController;
},
get aborted() {
return lazyAbortController().aborted;
},
types,
};
defineLazyProperties(
module.exports,
'internal/util/parse_args/parse_args',
['parseArgs'],
);
defineLazyProperties(
module.exports,
'internal/encoding',
['TextDecoder', 'TextEncoder'],
);
defineLazyProperties(
module.exports,
'internal/mime',
['MIMEType', 'MIMEParams'],
);