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