1'use strict'; 2 3const { 4 ArrayFrom, 5 ArrayIsArray, 6 Error, 7 Map, 8 ObjectCreate, 9 ObjectDefineProperties, 10 ObjectDefineProperty, 11 ObjectGetOwnPropertyDescriptor, 12 ObjectGetOwnPropertyDescriptors, 13 ObjectGetPrototypeOf, 14 ObjectSetPrototypeOf, 15 Promise, 16 ReflectConstruct, 17 Set, 18 Symbol, 19 SymbolFor, 20} = primordials; 21 22const { 23 codes: { 24 ERR_INVALID_ARG_TYPE, 25 ERR_NO_CRYPTO, 26 ERR_UNKNOWN_SIGNAL 27 }, 28 uvErrmapGet, 29 overrideStackTrace, 30} = require('internal/errors'); 31const { signals } = internalBinding('constants').os; 32const { 33 getHiddenValue, 34 setHiddenValue, 35 arrow_message_private_symbol: kArrowMessagePrivateSymbolIndex, 36 decorated_private_symbol: kDecoratedPrivateSymbolIndex, 37 sleep: _sleep 38} = internalBinding('util'); 39const { isNativeError } = internalBinding('types'); 40 41const noCrypto = !process.versions.openssl; 42 43const experimentalWarnings = new Set(); 44 45const colorRegExp = /\u001b\[\d\d?m/g; // eslint-disable-line no-control-regex 46 47function removeColors(str) { 48 return str.replace(colorRegExp, ''); 49} 50 51function isError(e) { 52 // An error could be an instance of Error while not being a native error 53 // or could be from a different realm and not be instance of Error but still 54 // be a native error. 55 return isNativeError(e) || e instanceof Error; 56} 57 58// Keep a list of deprecation codes that have been warned on so we only warn on 59// each one once. 60const codesWarned = new Set(); 61 62// Mark that a method should not be used. 63// Returns a modified function which warns once by default. 64// If --no-deprecation is set, then it is a no-op. 65function deprecate(fn, msg, code) { 66 if (process.noDeprecation === true) { 67 return fn; 68 } 69 70 if (code !== undefined && typeof code !== 'string') 71 throw new ERR_INVALID_ARG_TYPE('code', 'string', code); 72 73 let warned = false; 74 function deprecated(...args) { 75 if (!warned) { 76 warned = true; 77 if (code !== undefined) { 78 if (!codesWarned.has(code)) { 79 process.emitWarning(msg, 'DeprecationWarning', code, deprecated); 80 codesWarned.add(code); 81 } 82 } else { 83 process.emitWarning(msg, 'DeprecationWarning', deprecated); 84 } 85 } 86 if (new.target) { 87 return ReflectConstruct(fn, args, new.target); 88 } 89 return fn.apply(this, args); 90 } 91 92 // The wrapper will keep the same prototype as fn to maintain prototype chain 93 ObjectSetPrototypeOf(deprecated, fn); 94 if (fn.prototype) { 95 // Setting this (rather than using Object.setPrototype, as above) ensures 96 // that calling the unwrapped constructor gives an instanceof the wrapped 97 // constructor. 98 deprecated.prototype = fn.prototype; 99 } 100 101 return deprecated; 102} 103 104function decorateErrorStack(err) { 105 if (!(isError(err) && err.stack) || 106 getHiddenValue(err, kDecoratedPrivateSymbolIndex) === true) 107 return; 108 109 const arrow = getHiddenValue(err, kArrowMessagePrivateSymbolIndex); 110 111 if (arrow) { 112 err.stack = arrow + err.stack; 113 setHiddenValue(err, kDecoratedPrivateSymbolIndex, true); 114 } 115} 116 117function assertCrypto() { 118 if (noCrypto) 119 throw new ERR_NO_CRYPTO(); 120} 121 122// Return undefined if there is no match. 123// Move the "slow cases" to a separate function to make sure this function gets 124// inlined properly. That prioritizes the common case. 125function normalizeEncoding(enc) { 126 if (enc == null || enc === 'utf8' || enc === 'utf-8') return 'utf8'; 127 return slowCases(enc); 128} 129 130function slowCases(enc) { 131 switch (enc.length) { 132 case 4: 133 if (enc === 'UTF8') return 'utf8'; 134 if (enc === 'ucs2' || enc === 'UCS2') return 'utf16le'; 135 enc = `${enc}`.toLowerCase(); 136 if (enc === 'utf8') return 'utf8'; 137 if (enc === 'ucs2') return 'utf16le'; 138 break; 139 case 3: 140 if (enc === 'hex' || enc === 'HEX' || `${enc}`.toLowerCase() === 'hex') 141 return 'hex'; 142 break; 143 case 5: 144 if (enc === 'ascii') return 'ascii'; 145 if (enc === 'ucs-2') return 'utf16le'; 146 if (enc === 'UTF-8') return 'utf8'; 147 if (enc === 'ASCII') return 'ascii'; 148 if (enc === 'UCS-2') return 'utf16le'; 149 enc = `${enc}`.toLowerCase(); 150 if (enc === 'utf-8') return 'utf8'; 151 if (enc === 'ascii') return 'ascii'; 152 if (enc === 'ucs-2') return 'utf16le'; 153 break; 154 case 6: 155 if (enc === 'base64') return 'base64'; 156 if (enc === 'latin1' || enc === 'binary') return 'latin1'; 157 if (enc === 'BASE64') return 'base64'; 158 if (enc === 'LATIN1' || enc === 'BINARY') return 'latin1'; 159 enc = `${enc}`.toLowerCase(); 160 if (enc === 'base64') return 'base64'; 161 if (enc === 'latin1' || enc === 'binary') return 'latin1'; 162 break; 163 case 7: 164 if (enc === 'utf16le' || enc === 'UTF16LE' || 165 `${enc}`.toLowerCase() === 'utf16le') 166 return 'utf16le'; 167 break; 168 case 8: 169 if (enc === 'utf-16le' || enc === 'UTF-16LE' || 170 `${enc}`.toLowerCase() === 'utf-16le') 171 return 'utf16le'; 172 break; 173 default: 174 if (enc === '') return 'utf8'; 175 } 176} 177 178function emitExperimentalWarning(feature) { 179 if (experimentalWarnings.has(feature)) return; 180 const msg = `${feature} is an experimental feature. This feature could ` + 181 'change at any time'; 182 experimentalWarnings.add(feature); 183 process.emitWarning(msg, 'ExperimentalWarning'); 184} 185 186function filterDuplicateStrings(items, low) { 187 const map = new Map(); 188 for (let i = 0; i < items.length; i++) { 189 const item = items[i]; 190 const key = item.toLowerCase(); 191 if (low) { 192 map.set(key, key); 193 } else { 194 map.set(key, item); 195 } 196 } 197 return ArrayFrom(map.values()).sort(); 198} 199 200function cachedResult(fn) { 201 let result; 202 return () => { 203 if (result === undefined) 204 result = fn(); 205 return result.slice(); 206 }; 207} 208 209// Useful for Wrapping an ES6 Class with a constructor Function that 210// does not require the new keyword. For instance: 211// class A { constructor(x) {this.x = x;}} 212// const B = createClassWrapper(A); 213// B() instanceof A // true 214// B() instanceof B // true 215function createClassWrapper(type) { 216 function fn(...args) { 217 return ReflectConstruct(type, args, new.target || type); 218 } 219 // Mask the wrapper function name and length values 220 ObjectDefineProperties(fn, { 221 name: { value: type.name }, 222 length: { value: type.length } 223 }); 224 ObjectSetPrototypeOf(fn, type); 225 fn.prototype = type.prototype; 226 return fn; 227} 228 229let signalsToNamesMapping; 230function getSignalsToNamesMapping() { 231 if (signalsToNamesMapping !== undefined) 232 return signalsToNamesMapping; 233 234 signalsToNamesMapping = ObjectCreate(null); 235 for (const key in signals) { 236 signalsToNamesMapping[signals[key]] = key; 237 } 238 239 return signalsToNamesMapping; 240} 241 242function convertToValidSignal(signal) { 243 if (typeof signal === 'number' && getSignalsToNamesMapping()[signal]) 244 return signal; 245 246 if (typeof signal === 'string') { 247 const signalName = signals[signal.toUpperCase()]; 248 if (signalName) return signalName; 249 } 250 251 throw new ERR_UNKNOWN_SIGNAL(signal); 252} 253 254function getConstructorOf(obj) { 255 while (obj) { 256 const descriptor = ObjectGetOwnPropertyDescriptor(obj, 'constructor'); 257 if (descriptor !== undefined && 258 typeof descriptor.value === 'function' && 259 descriptor.value.name !== '') { 260 return descriptor.value; 261 } 262 263 obj = ObjectGetPrototypeOf(obj); 264 } 265 266 return null; 267} 268 269function getSystemErrorName(err) { 270 const entry = uvErrmapGet(err); 271 return entry ? entry[0] : `Unknown system error ${err}`; 272} 273 274const kCustomPromisifiedSymbol = SymbolFor('nodejs.util.promisify.custom'); 275const kCustomPromisifyArgsSymbol = Symbol('customPromisifyArgs'); 276 277function promisify(original) { 278 if (typeof original !== 'function') 279 throw new ERR_INVALID_ARG_TYPE('original', 'Function', original); 280 281 if (original[kCustomPromisifiedSymbol]) { 282 const fn = original[kCustomPromisifiedSymbol]; 283 if (typeof fn !== 'function') { 284 throw new ERR_INVALID_ARG_TYPE('util.promisify.custom', 'Function', fn); 285 } 286 return ObjectDefineProperty(fn, kCustomPromisifiedSymbol, { 287 value: fn, enumerable: false, writable: false, configurable: true 288 }); 289 } 290 291 // Names to create an object from in case the callback receives multiple 292 // arguments, e.g. ['bytesRead', 'buffer'] for fs.read. 293 const argumentNames = original[kCustomPromisifyArgsSymbol]; 294 295 function fn(...args) { 296 return new Promise((resolve, reject) => { 297 original.call(this, ...args, (err, ...values) => { 298 if (err) { 299 return reject(err); 300 } 301 if (argumentNames !== undefined && values.length > 1) { 302 const obj = {}; 303 for (let i = 0; i < argumentNames.length; i++) 304 obj[argumentNames[i]] = values[i]; 305 resolve(obj); 306 } else { 307 resolve(values[0]); 308 } 309 }); 310 }); 311 } 312 313 ObjectSetPrototypeOf(fn, ObjectGetPrototypeOf(original)); 314 315 ObjectDefineProperty(fn, kCustomPromisifiedSymbol, { 316 value: fn, enumerable: false, writable: false, configurable: true 317 }); 318 return ObjectDefineProperties( 319 fn, 320 ObjectGetOwnPropertyDescriptors(original) 321 ); 322} 323 324promisify.custom = kCustomPromisifiedSymbol; 325 326// The build-in Array#join is slower in v8 6.0 327function join(output, separator) { 328 let str = ''; 329 if (output.length !== 0) { 330 const lastIndex = output.length - 1; 331 for (let i = 0; i < lastIndex; i++) { 332 // It is faster not to use a template string here 333 str += output[i]; 334 str += separator; 335 } 336 str += output[lastIndex]; 337 } 338 return str; 339} 340 341// As of V8 6.6, depending on the size of the array, this is anywhere 342// between 1.5-10x faster than the two-arg version of Array#splice() 343function spliceOne(list, index) { 344 for (; index + 1 < list.length; index++) 345 list[index] = list[index + 1]; 346 list.pop(); 347} 348 349const kNodeModulesRE = /^(.*)[\\/]node_modules[\\/]/; 350 351let getStructuredStack; 352 353function isInsideNodeModules() { 354 if (getStructuredStack === undefined) { 355 // Lazy-load to avoid a circular dependency. 356 const { runInNewContext } = require('vm'); 357 // Use `runInNewContext()` to get something tamper-proof and 358 // side-effect-free. Since this is currently only used for a deprecated API, 359 // the perf implications should be okay. 360 getStructuredStack = runInNewContext(`(function() { 361 Error.stackTraceLimit = Infinity; 362 return function structuredStack() { 363 const e = new Error(); 364 overrideStackTrace.set(e, (err, trace) => trace); 365 return e.stack; 366 }; 367 })()`, { overrideStackTrace }, { filename: 'structured-stack' }); 368 } 369 370 const stack = getStructuredStack(); 371 372 // Iterate over all stack frames and look for the first one not coming 373 // from inside Node.js itself: 374 if (ArrayIsArray(stack)) { 375 for (const frame of stack) { 376 const filename = frame.getFileName(); 377 // If a filename does not start with / or contain \, 378 // it's likely from Node.js core. 379 if (!/^\/|\\/.test(filename)) 380 continue; 381 return kNodeModulesRE.test(filename); 382 } 383 } 384 return false; 385} 386 387function once(callback) { 388 let called = false; 389 return function(...args) { 390 if (called) return; 391 called = true; 392 callback.apply(this, args); 393 }; 394} 395 396let validateUint32; 397 398function sleep(msec) { 399 // Lazy-load to avoid a circular dependency. 400 if (validateUint32 === undefined) 401 ({ validateUint32 } = require('internal/validators')); 402 403 validateUint32(msec, 'msec'); 404 _sleep(msec); 405} 406 407module.exports = { 408 assertCrypto, 409 cachedResult, 410 convertToValidSignal, 411 createClassWrapper, 412 decorateErrorStack, 413 deprecate, 414 emitExperimentalWarning, 415 filterDuplicateStrings, 416 getConstructorOf, 417 getSystemErrorName, 418 isError, 419 isInsideNodeModules, 420 join, 421 normalizeEncoding, 422 once, 423 promisify, 424 sleep, 425 spliceOne, 426 removeColors, 427 428 // Symbol used to customize promisify conversion 429 customPromisifyArgs: kCustomPromisifyArgsSymbol, 430 431 // Symbol used to provide a custom inspect function for an object as an 432 // alternative to using 'inspect' 433 customInspectSymbol: SymbolFor('nodejs.util.inspect.custom'), 434 435 // Used by the buffer module to capture an internal reference to the 436 // default isEncoding implementation, just in case userland overrides it. 437 kIsEncodingSymbol: Symbol('kIsEncodingSymbol'), 438 kVmBreakFirstLineSymbol: Symbol('kVmBreakFirstLineSymbol') 439}; 440