Merge remote-tracking branch 'upstream/v0.10'
[platform/upstream/nodejs.git] / lib / util.js
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 var formatRegExp = /%[sdj%]/g;
23 exports.format = function(f) {
24   if (!isString(f)) {
25     var objects = [];
26     for (var i = 0; i < arguments.length; i++) {
27       objects.push(inspect(arguments[i]));
28     }
29     return objects.join(' ');
30   }
31
32   var i = 1;
33   var args = arguments;
34   var len = args.length;
35   var str = String(f).replace(formatRegExp, function(x) {
36     if (x === '%%') return '%';
37     if (i >= len) return x;
38     switch (x) {
39       case '%s': return String(args[i++]);
40       case '%d': return Number(args[i++]);
41       case '%j':
42         try {
43           return JSON.stringify(args[i++]);
44         } catch (_) {
45           return '[Circular]';
46         }
47       default:
48         return x;
49     }
50   });
51   for (var x = args[i]; i < len; x = args[++i]) {
52     if (isNull(x) || !isObject(x)) {
53       str += ' ' + x;
54     } else {
55       str += ' ' + inspect(x);
56     }
57   }
58   return str;
59 };
60
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.
65 exports.deprecate = function(fn, msg) {
66   // Allow for deprecating things in the process of starting up.
67   if (isUndefined(global.process)) {
68     return function() {
69       return exports.deprecate(fn, msg).apply(this, arguments);
70     };
71   }
72
73   if (process.noDeprecation === true) {
74     return fn;
75   }
76
77   var warned = false;
78   function deprecated() {
79     if (!warned) {
80       if (process.throwDeprecation) {
81         throw new Error(msg);
82       } else if (process.traceDeprecation) {
83         console.trace(msg);
84       } else {
85         console.error(msg);
86       }
87       warned = true;
88     }
89     return fn.apply(this, arguments);
90   }
91
92   return deprecated;
93 };
94
95
96 var debugs = {};
97 var debugEnviron;
98 exports.debuglog = function(set) {
99   if (isUndefined(debugEnviron))
100     debugEnviron = process.env.NODE_DEBUG || '';
101   set = set.toUpperCase();
102   if (!debugs[set]) {
103     if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
104       var pid = process.pid;
105       debugs[set] = function() {
106         var msg = exports.format.apply(exports, arguments);
107         console.error('%s %d: %s', set, pid, msg);
108       };
109     } else {
110       debugs[set] = function() {};
111     }
112   }
113   return debugs[set];
114 };
115
116
117 /**
118  * Echos the value of a value. Trys to print the value out
119  * in the best way possible given the different types.
120  *
121  * @param {Object} obj The object to print out.
122  * @param {Object} opts Optional options object that alters the output.
123  */
124 /* legacy: obj, showHidden, depth, colors*/
125 function inspect(obj, opts) {
126   // default options
127   var ctx = {
128     seen: [],
129     stylize: stylizeNoColor
130   };
131   // legacy...
132   if (arguments.length >= 3) ctx.depth = arguments[2];
133   if (arguments.length >= 4) ctx.colors = arguments[3];
134   if (isBoolean(opts)) {
135     // legacy...
136     ctx.showHidden = opts;
137   } else if (opts) {
138     // got an "options" object
139     exports._extend(ctx, opts);
140   }
141   // set default options
142   if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
143   if (isUndefined(ctx.depth)) ctx.depth = 2;
144   if (isUndefined(ctx.colors)) ctx.colors = false;
145   if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
146   if (ctx.colors) ctx.stylize = stylizeWithColor;
147   return formatValue(ctx, obj, ctx.depth);
148 }
149 exports.inspect = inspect;
150
151
152 // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
153 inspect.colors = {
154   'bold' : [1, 22],
155   'italic' : [3, 23],
156   'underline' : [4, 24],
157   'inverse' : [7, 27],
158   'white' : [37, 39],
159   'grey' : [90, 39],
160   'black' : [30, 39],
161   'blue' : [34, 39],
162   'cyan' : [36, 39],
163   'green' : [32, 39],
164   'magenta' : [35, 39],
165   'red' : [31, 39],
166   'yellow' : [33, 39]
167 };
168
169 // Don't use 'blue' not visible on cmd.exe
170 inspect.styles = {
171   'special': 'cyan',
172   'number': 'yellow',
173   'boolean': 'yellow',
174   'undefined': 'grey',
175   'null': 'bold',
176   'string': 'green',
177   'date': 'magenta',
178   // "name": intentionally not styling
179   'regexp': 'red'
180 };
181
182
183 function stylizeWithColor(str, styleType) {
184   var style = inspect.styles[styleType];
185
186   if (style) {
187     return '\u001b[' + inspect.colors[style][0] + 'm' + str +
188            '\u001b[' + inspect.colors[style][1] + 'm';
189   } else {
190     return str;
191   }
192 }
193
194
195 function stylizeNoColor(str, styleType) {
196   return str;
197 }
198
199
200 function arrayToHash(array) {
201   var hash = {};
202
203   array.forEach(function(val, idx) {
204     hash[val] = true;
205   });
206
207   return hash;
208 }
209
210
211 function formatValue(ctx, value, recurseTimes) {
212   // Provide a hook for user-specified inspect functions.
213   // Check that value is an object with an inspect function on it
214   if (ctx.customInspect &&
215       value &&
216       isFunction(value.inspect) &&
217       // Filter out the util module, it's inspect function is special
218       value.inspect !== exports.inspect &&
219       // Also filter out any prototype objects using the circular check.
220       !(value.constructor && value.constructor.prototype === value)) {
221     var ret = value.inspect(recurseTimes, ctx);
222     if (!isString(ret)) {
223       ret = formatValue(ctx, ret, recurseTimes);
224     }
225     return ret;
226   }
227
228   // Primitive types cannot have properties
229   var primitive = formatPrimitive(ctx, value);
230   if (primitive) {
231     return primitive;
232   }
233
234   // Look up the keys of the object.
235   var keys = Object.keys(value);
236   var visibleKeys = arrayToHash(keys);
237
238   if (ctx.showHidden) {
239     keys = Object.getOwnPropertyNames(value);
240   }
241
242   // Some type of object without properties can be shortcutted.
243   if (keys.length === 0) {
244     if (isFunction(value)) {
245       var name = value.name ? ': ' + value.name : '';
246       return ctx.stylize('[Function' + name + ']', 'special');
247     }
248     if (isRegExp(value)) {
249       return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
250     }
251     if (isDate(value)) {
252       return ctx.stylize(Date.prototype.toString.call(value), 'date');
253     }
254     if (isError(value)) {
255       return formatError(value);
256     }
257   }
258
259   var base = '', array = false, braces = ['{', '}'];
260
261   // Make Array say that they are Array
262   if (isArray(value)) {
263     array = true;
264     braces = ['[', ']'];
265   }
266
267   // Make functions say that they are functions
268   if (isFunction(value)) {
269     var n = value.name ? ': ' + value.name : '';
270     base = ' [Function' + n + ']';
271   }
272
273   // Make RegExps say that they are RegExps
274   if (isRegExp(value)) {
275     base = ' ' + RegExp.prototype.toString.call(value);
276   }
277
278   // Make dates with properties first say the date
279   if (isDate(value)) {
280     base = ' ' + Date.prototype.toUTCString.call(value);
281   }
282
283   // Make error with message first say the error
284   if (isError(value)) {
285     base = ' ' + formatError(value);
286   }
287
288   if (keys.length === 0 && (!array || value.length == 0)) {
289     return braces[0] + base + braces[1];
290   }
291
292   if (recurseTimes < 0) {
293     if (isRegExp(value)) {
294       return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
295     } else {
296       return ctx.stylize('[Object]', 'special');
297     }
298   }
299
300   ctx.seen.push(value);
301
302   var output;
303   if (array) {
304     output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
305   } else {
306     output = keys.map(function(key) {
307       return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
308     });
309   }
310
311   ctx.seen.pop();
312
313   return reduceToSingleString(output, base, braces);
314 }
315
316
317 function formatPrimitive(ctx, value) {
318   if (isUndefined(value))
319     return ctx.stylize('undefined', 'undefined');
320   if (isString(value)) {
321     var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
322                                              .replace(/'/g, "\\'")
323                                              .replace(/\\"/g, '"') + '\'';
324     return ctx.stylize(simple, 'string');
325   }
326   if (isNumber(value)) {
327     // Format -0 as '-0'. Strict equality won't distinguish 0 from -0,
328     // so instead we use the fact that 1 / -0 < 0 whereas 1 / 0 > 0 .
329     if (value === 0 && 1 / value < 0)
330       return ctx.stylize('-0', 'number');
331     return ctx.stylize('' + value, 'number');
332   }
333   if (isBoolean(value))
334     return ctx.stylize('' + value, 'boolean');
335   // For some reason typeof null is "object", so special case here.
336   if (isNull(value))
337     return ctx.stylize('null', 'null');
338 }
339
340
341 function formatError(value) {
342   return '[' + Error.prototype.toString.call(value) + ']';
343 }
344
345
346 function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
347   var output = [];
348   for (var i = 0, l = value.length; i < l; ++i) {
349     if (hasOwnProperty(value, String(i))) {
350       output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
351           String(i), true));
352     } else {
353       output.push('');
354     }
355   }
356   keys.forEach(function(key) {
357     if (!key.match(/^\d+$/)) {
358       output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
359           key, true));
360     }
361   });
362   return output;
363 }
364
365
366 function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
367   var name, str, desc;
368   desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
369   if (desc.get) {
370     if (desc.set) {
371       str = ctx.stylize('[Getter/Setter]', 'special');
372     } else {
373       str = ctx.stylize('[Getter]', 'special');
374     }
375   } else {
376     if (desc.set) {
377       str = ctx.stylize('[Setter]', 'special');
378     }
379   }
380   if (!hasOwnProperty(visibleKeys, key)) {
381     name = '[' + key + ']';
382   }
383   if (!str) {
384     if (ctx.seen.indexOf(desc.value) < 0) {
385       if (isNull(recurseTimes)) {
386         str = formatValue(ctx, desc.value, null);
387       } else {
388         str = formatValue(ctx, desc.value, recurseTimes - 1);
389       }
390       if (str.indexOf('\n') > -1) {
391         if (array) {
392           str = str.split('\n').map(function(line) {
393             return '  ' + line;
394           }).join('\n').substr(2);
395         } else {
396           str = '\n' + str.split('\n').map(function(line) {
397             return '   ' + line;
398           }).join('\n');
399         }
400       }
401     } else {
402       str = ctx.stylize('[Circular]', 'special');
403     }
404   }
405   if (isUndefined(name)) {
406     if (array && key.match(/^\d+$/)) {
407       return str;
408     }
409     name = JSON.stringify('' + key);
410     if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
411       name = name.substr(1, name.length - 2);
412       name = ctx.stylize(name, 'name');
413     } else {
414       name = name.replace(/'/g, "\\'")
415                  .replace(/\\"/g, '"')
416                  .replace(/(^"|"$)/g, "'")
417                  .replace(/\\\\/g, '\\');
418       name = ctx.stylize(name, 'string');
419     }
420   }
421
422   return name + ': ' + str;
423 }
424
425
426 function reduceToSingleString(output, base, braces) {
427   var numLinesEst = 0;
428   var length = output.reduce(function(prev, cur) {
429     numLinesEst++;
430     if (cur.indexOf('\n') >= 0) numLinesEst++;
431     return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
432   }, 0);
433
434   if (length > 60) {
435     return braces[0] +
436            (base === '' ? '' : base + '\n ') +
437            ' ' +
438            output.join(',\n  ') +
439            ' ' +
440            braces[1];
441   }
442
443   return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
444 }
445
446
447 // NOTE: These type checking functions intentionally don't use `instanceof`
448 // because it is fragile and can be easily faked with `Object.create()`.
449 function isArray(ar) {
450   return Array.isArray(ar);
451 }
452 exports.isArray = isArray;
453
454 function isBoolean(arg) {
455   return typeof arg === 'boolean';
456 }
457 exports.isBoolean = isBoolean;
458
459 function isNull(arg) {
460   return arg === null;
461 }
462 exports.isNull = isNull;
463
464 function isNullOrUndefined(arg) {
465   return arg == null;
466 }
467 exports.isNullOrUndefined = isNullOrUndefined;
468
469 function isNumber(arg) {
470   return typeof arg === 'number';
471 }
472 exports.isNumber = isNumber;
473
474 function isString(arg) {
475   return typeof arg === 'string';
476 }
477 exports.isString = isString;
478
479 function isSymbol(arg) {
480   return typeof arg === 'symbol';
481 }
482 exports.isSymbol = isSymbol;
483
484 function isUndefined(arg) {
485   return arg === void 0;
486 }
487 exports.isUndefined = isUndefined;
488
489 function isRegExp(re) {
490   return isObject(re) && objectToString(re) === '[object RegExp]';
491 }
492 exports.isRegExp = isRegExp;
493
494 function isObject(arg) {
495   return typeof arg === 'object' && arg !== null;
496 }
497 exports.isObject = isObject;
498
499 function isDate(d) {
500   return isObject(d) && objectToString(d) === '[object Date]';
501 }
502 exports.isDate = isDate;
503
504 function isError(e) {
505   return isObject(e) &&
506       (objectToString(e) === '[object Error]' || e instanceof Error);
507 }
508 exports.isError = isError;
509
510 function isFunction(arg) {
511   return typeof arg === 'function';
512 }
513 exports.isFunction = isFunction;
514
515 function isPrimitive(arg) {
516   return arg === null ||
517          typeof arg === 'boolean' ||
518          typeof arg === 'number' ||
519          typeof arg === 'string' ||
520          typeof arg === 'symbol' ||  // ES6 symbol
521          typeof arg === 'undefined';
522 }
523 exports.isPrimitive = isPrimitive;
524
525 function isBuffer(arg) {
526   return arg instanceof Buffer;
527 }
528 exports.isBuffer = isBuffer;
529
530 function objectToString(o) {
531   return Object.prototype.toString.call(o);
532 }
533
534
535 function pad(n) {
536   return n < 10 ? '0' + n.toString(10) : n.toString(10);
537 }
538
539
540 var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
541               'Oct', 'Nov', 'Dec'];
542
543 // 26 Feb 16:19:34
544 function timestamp() {
545   var d = new Date();
546   var time = [pad(d.getHours()),
547               pad(d.getMinutes()),
548               pad(d.getSeconds())].join(':');
549   return [d.getDate(), months[d.getMonth()], time].join(' ');
550 }
551
552
553 // log is just a thin wrapper to console.log that prepends a timestamp
554 exports.log = function() {
555   console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
556 };
557
558
559 /**
560  * Inherit the prototype methods from one constructor into another.
561  *
562  * The Function.prototype.inherits from lang.js rewritten as a standalone
563  * function (not on Function.prototype). NOTE: If this file is to be loaded
564  * during bootstrapping this function needs to be rewritten using some native
565  * functions as prototype setup using normal JavaScript does not work as
566  * expected during bootstrapping (see mirror.js in r114903).
567  *
568  * @param {function} ctor Constructor function which needs to inherit the
569  *     prototype.
570  * @param {function} superCtor Constructor function to inherit prototype from.
571  */
572 exports.inherits = function(ctor, superCtor) {
573   ctor.super_ = superCtor;
574   ctor.prototype = Object.create(superCtor.prototype, {
575     constructor: {
576       value: ctor,
577       enumerable: false,
578       writable: true,
579       configurable: true
580     }
581   });
582 };
583
584 exports._extend = function(origin, add) {
585   // Don't do anything if add isn't an object
586   if (!add || !isObject(add)) return origin;
587
588   var keys = Object.keys(add);
589   var i = keys.length;
590   while (i--) {
591     origin[keys[i]] = add[keys[i]];
592   }
593   return origin;
594 };
595
596 function hasOwnProperty(obj, prop) {
597   return Object.prototype.hasOwnProperty.call(obj, prop);
598 }
599
600
601 // Deprecated old stuff.
602
603 exports.p = exports.deprecate(function() {
604   for (var i = 0, len = arguments.length; i < len; ++i) {
605     console.error(exports.inspect(arguments[i]));
606   }
607 }, 'util.p: Use console.error() instead');
608
609
610 exports.exec = exports.deprecate(function() {
611   return require('child_process').exec.apply(this, arguments);
612 }, 'util.exec is now called `child_process.exec`.');
613
614
615 exports.print = exports.deprecate(function() {
616   for (var i = 0, len = arguments.length; i < len; ++i) {
617     process.stdout.write(String(arguments[i]));
618   }
619 }, 'util.print: Use console.log instead');
620
621
622 exports.puts = exports.deprecate(function() {
623   for (var i = 0, len = arguments.length; i < len; ++i) {
624     process.stdout.write(arguments[i] + '\n');
625   }
626 }, 'util.puts: Use console.log instead');
627
628
629 exports.debug = exports.deprecate(function(x) {
630   process.stderr.write('DEBUG: ' + x + '\n');
631 }, 'util.debug: Use console.error instead');
632
633
634 exports.error = exports.deprecate(function(x) {
635   for (var i = 0, len = arguments.length; i < len; ++i) {
636     process.stderr.write(arguments[i] + '\n');
637   }
638 }, 'util.error: Use console.error instead');
639
640
641 exports.pump = exports.deprecate(function(readStream, writeStream, callback) {
642   var callbackCalled = false;
643
644   function call(a, b, c) {
645     if (callback && !callbackCalled) {
646       callback(a, b, c);
647       callbackCalled = true;
648     }
649   }
650
651   readStream.addListener('data', function(chunk) {
652     if (writeStream.write(chunk) === false) readStream.pause();
653   });
654
655   writeStream.addListener('drain', function() {
656     readStream.resume();
657   });
658
659   readStream.addListener('end', function() {
660     writeStream.end();
661   });
662
663   readStream.addListener('close', function() {
664     call();
665   });
666
667   readStream.addListener('error', function(err) {
668     writeStream.end();
669     call(err);
670   });
671
672   writeStream.addListener('error', function(err) {
673     readStream.destroy();
674     call(err);
675   });
676 }, 'util.pump(): Use readableStream.pipe() instead');
677
678
679 var uv;
680 exports._errnoException = function(err, syscall) {
681   if (isUndefined(uv)) uv = process.binding('uv');
682   var errname = uv.errname(err);
683   var e = new Error(syscall + ' ' + errname);
684   e.code = errname;
685   e.errno = errname;
686   e.syscall = syscall;
687   return e;
688 };