0
0
mirror of https://github.com/nodejs/node.git synced 2024-12-01 16:10:02 +01:00
nodejs/lib/internal/util.js
James M Snell ef16319eff util: fixup internal util exports
util.promisify landed without using the module.exports = {}
pattern. This fixes it up for consistency

PR-URL: https://github.com/nodejs/node/pull/12998
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
2017-05-15 08:26:57 -07:00

282 lines
7.7 KiB
JavaScript

'use strict';
const errors = require('internal/errors');
const binding = process.binding('util');
const signals = process.binding('constants').os.signals;
const { createPromise, promiseResolve, promiseReject } = binding;
const kArrowMessagePrivateSymbolIndex = binding['arrow_message_private_symbol'];
const kDecoratedPrivateSymbolIndex = binding['decorated_private_symbol'];
const noCrypto = !process.versions.openssl;
function isError(e) {
return objectToString(e) === '[object Error]' || e instanceof Error;
}
function objectToString(o) {
return Object.prototype.toString.call(o);
}
// Mark that a method should not be used.
// Returns a modified function which warns once by default.
// If --no-deprecation is set, then it is a no-op.
function deprecate(fn, msg, code) {
// Allow for deprecating things in the process of starting up.
if (global.process === undefined) {
return function(...args) {
return deprecate(fn, msg).apply(this, args);
};
}
if (process.noDeprecation === true) {
return fn;
}
if (code !== undefined && typeof code !== 'string')
throw new TypeError('`code` argument must be a string');
var warned = false;
function deprecated(...args) {
if (!warned) {
warned = true;
if (code !== undefined) {
process.emitWarning(msg, 'DeprecationWarning', code, deprecated);
} else {
process.emitWarning(msg, 'DeprecationWarning', deprecated);
}
}
if (new.target) {
return Reflect.construct(fn, args, new.target);
}
return fn.apply(this, args);
}
// The wrapper will keep the same prototype as fn to maintain prototype chain
Object.setPrototypeOf(deprecated, fn);
if (fn.prototype) {
// Setting this (rather than using Object.setPrototype, as above) ensures
// that calling the unwrapped constructor gives an instanceof the wrapped
// constructor.
deprecated.prototype = fn.prototype;
}
return deprecated;
}
function decorateErrorStack(err) {
if (!(isError(err) && err.stack) ||
binding.getHiddenValue(err, kDecoratedPrivateSymbolIndex) === true)
return;
const arrow = binding.getHiddenValue(err, kArrowMessagePrivateSymbolIndex);
if (arrow) {
err.stack = arrow + err.stack;
binding.setHiddenValue(err, kDecoratedPrivateSymbolIndex, true);
}
}
function assertCrypto() {
if (noCrypto)
throw new Error('Node.js is not compiled with openssl crypto support');
}
// The loop should only run at most twice, retrying with lowercased enc
// if there is no match in the first pass.
// We use a loop instead of branching to retry with a helper
// function in order to avoid the performance hit.
// Return undefined if there is no match.
function normalizeEncoding(enc) {
if (!enc) return 'utf8';
var retried;
while (true) {
switch (enc) {
case 'utf8':
case 'utf-8':
return 'utf8';
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
return 'utf16le';
case 'latin1':
case 'binary':
return 'latin1';
case 'base64':
case 'ascii':
case 'hex':
return enc;
default:
if (retried) return; // undefined
enc = ('' + enc).toLowerCase();
retried = true;
}
}
}
function filterDuplicateStrings(items, low) {
const map = new Map();
for (var i = 0; i < items.length; i++) {
const item = items[i];
const key = item.toLowerCase();
if (low) {
map.set(key, key);
} else {
map.set(key, item);
}
}
return Array.from(map.values()).sort();
}
function cachedResult(fn) {
var result;
return () => {
if (result === undefined)
result = fn();
return result.slice();
};
}
// Useful for Wrapping an ES6 Class with a constructor Function that
// does not require the new keyword. For instance:
// class A { constructor(x) {this.x = x;}}
// const B = createClassWrapper(A);
// B() instanceof A // true
// B() instanceof B // true
function createClassWrapper(type) {
function fn(...args) {
return Reflect.construct(type, args, new.target || type);
}
// Mask the wrapper function name and length values
Object.defineProperties(fn, {
name: {value: type.name},
length: {value: type.length}
});
Object.setPrototypeOf(fn, type);
fn.prototype = type.prototype;
return fn;
}
let signalsToNamesMapping;
function getSignalsToNamesMapping() {
if (signalsToNamesMapping !== undefined)
return signalsToNamesMapping;
signalsToNamesMapping = Object.create(null);
for (var key in signals) {
signalsToNamesMapping[signals[key]] = key;
}
return signalsToNamesMapping;
}
function convertToValidSignal(signal) {
if (typeof signal === 'number' && getSignalsToNamesMapping()[signal])
return signal;
if (typeof signal === 'string') {
const signalName = signals[signal.toUpperCase()];
if (signalName) return signalName;
}
throw new errors.Error('ERR_UNKNOWN_SIGNAL', signal);
}
function getConstructorOf(obj) {
while (obj) {
var descriptor = Object.getOwnPropertyDescriptor(obj, 'constructor');
if (descriptor !== undefined &&
typeof descriptor.value === 'function' &&
descriptor.value.name !== '') {
return descriptor.value;
}
obj = Object.getPrototypeOf(obj);
}
return null;
}
const kCustomPromisifiedSymbol = Symbol('util.promisify.custom');
const kCustomPromisifyArgsSymbol = Symbol('customPromisifyArgs');
function promisify(orig) {
if (typeof orig !== 'function') {
const errors = require('internal/errors');
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'original', 'function');
}
if (orig[kCustomPromisifiedSymbol]) {
const fn = orig[kCustomPromisifiedSymbol];
if (typeof fn !== 'function') {
throw new TypeError('The [util.promisify.custom] property must be ' +
'a function');
}
Object.defineProperty(fn, kCustomPromisifiedSymbol, {
value: fn, enumerable: false, writable: false, configurable: true
});
return fn;
}
// Names to create an object from in case the callback receives multiple
// arguments, e.g. ['stdout', 'stderr'] for child_process.exec.
const argumentNames = orig[kCustomPromisifyArgsSymbol];
function fn(...args) {
const promise = createPromise();
try {
orig.call(this, ...args, (err, ...values) => {
if (err) {
promiseReject(promise, err);
} else if (argumentNames !== undefined && values.length > 1) {
const obj = {};
for (var i = 0; i < argumentNames.length; i++)
obj[argumentNames[i]] = values[i];
promiseResolve(promise, obj);
} else {
promiseResolve(promise, values[0]);
}
});
} catch (err) {
promiseReject(promise, err);
}
return promise;
}
Object.setPrototypeOf(fn, Object.getPrototypeOf(orig));
Object.defineProperty(fn, kCustomPromisifiedSymbol, {
value: fn, enumerable: false, writable: false, configurable: true
});
return Object.defineProperties(fn, Object.getOwnPropertyDescriptors(orig));
}
promisify.custom = kCustomPromisifiedSymbol;
module.exports = {
assertCrypto,
cachedResult,
convertToValidSignal,
createClassWrapper,
decorateErrorStack,
deprecate,
filterDuplicateStrings,
getConstructorOf,
isError,
normalizeEncoding,
objectToString,
promisify,
// Symbol used to customize promisify conversion
customPromisifyArgs: kCustomPromisifyArgsSymbol,
// Symbol used to provide a custom inspect function for an object as an
// alternative to using 'inspect'
customInspectSymbol: Symbol('util.inspect.custom'),
// Used by the buffer module to capture an internal reference to the
// default isEncoding implementation, just in case userland overrides it.
kIsEncodingSymbol: Symbol('node.isEncoding')
};