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     return ctx.stylize('' + value, 'number');
328   if (isBoolean(value))
329     return ctx.stylize('' + value, 'boolean');
330   // For some reason typeof null is "object", so special case here.
331   if (isNull(value))
332     return ctx.stylize('null', 'null');
333 }
334
335
336 function formatError(value) {
337   return '[' + Error.prototype.toString.call(value) + ']';
338 }
339
340
341 function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
342   var output = [];
343   for (var i = 0, l = value.length; i < l; ++i) {
344     if (hasOwnProperty(value, String(i))) {
345       output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
346           String(i), true));
347     } else {
348       output.push('');
349     }
350   }
351   keys.forEach(function(key) {
352     if (!key.match(/^\d+$/)) {
353       output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
354           key, true));
355     }
356   });
357   return output;
358 }
359
360
361 function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
362   var name, str, desc;
363   desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
364   if (desc.get) {
365     if (desc.set) {
366       str = ctx.stylize('[Getter/Setter]', 'special');
367     } else {
368       str = ctx.stylize('[Getter]', 'special');
369     }
370   } else {
371     if (desc.set) {
372       str = ctx.stylize('[Setter]', 'special');
373     }
374   }
375   if (!hasOwnProperty(visibleKeys, key)) {
376     name = '[' + key + ']';
377   }
378   if (!str) {
379     if (ctx.seen.indexOf(desc.value) < 0) {
380       if (isNull(recurseTimes)) {
381         str = formatValue(ctx, desc.value, null);
382       } else {
383         str = formatValue(ctx, desc.value, recurseTimes - 1);
384       }
385       if (str.indexOf('\n') > -1) {
386         if (array) {
387           str = str.split('\n').map(function(line) {
388             return '  ' + line;
389           }).join('\n').substr(2);
390         } else {
391           str = '\n' + str.split('\n').map(function(line) {
392             return '   ' + line;
393           }).join('\n');
394         }
395       }
396     } else {
397       str = ctx.stylize('[Circular]', 'special');
398     }
399   }
400   if (isUndefined(name)) {
401     if (array && key.match(/^\d+$/)) {
402       return str;
403     }
404     name = JSON.stringify('' + key);
405     if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
406       name = name.substr(1, name.length - 2);
407       name = ctx.stylize(name, 'name');
408     } else {
409       name = name.replace(/'/g, "\\'")
410                  .replace(/\\"/g, '"')
411                  .replace(/(^"|"$)/g, "'");
412       name = ctx.stylize(name, 'string');
413     }
414   }
415
416   return name + ': ' + str;
417 }
418
419
420 function reduceToSingleString(output, base, braces) {
421   var numLinesEst = 0;
422   var length = output.reduce(function(prev, cur) {
423     numLinesEst++;
424     if (cur.indexOf('\n') >= 0) numLinesEst++;
425     return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
426   }, 0);
427
428   if (length > 60) {
429     return braces[0] +
430            (base === '' ? '' : base + '\n ') +
431            ' ' +
432            output.join(',\n  ') +
433            ' ' +
434            braces[1];
435   }
436
437   return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
438 }
439
440
441 // NOTE: These type checking functions intentionally don't use `instanceof`
442 // because it is fragile and can be easily faked with `Object.create()`.
443 function isArray(ar) {
444   return Array.isArray(ar);
445 }
446 exports.isArray = isArray;
447
448 function isBoolean(arg) {
449   return typeof arg === 'boolean';
450 }
451 exports.isBoolean = isBoolean;
452
453 function isNull(arg) {
454   return arg === null;
455 }
456 exports.isNull = isNull;
457
458 function isNullOrUndefined(arg) {
459   return arg == null;
460 }
461 exports.isNullOrUndefined = isNullOrUndefined;
462
463 function isNumber(arg) {
464   return typeof arg === 'number';
465 }
466 exports.isNumber = isNumber;
467
468 function isString(arg) {
469   return typeof arg === 'string';
470 }
471 exports.isString = isString;
472
473 function isSymbol(arg) {
474   return typeof arg === 'symbol';
475 }
476 exports.isSymbol = isSymbol;
477
478 function isUndefined(arg) {
479   return arg === void 0;
480 }
481 exports.isUndefined = isUndefined;
482
483 function isRegExp(re) {
484   return isObject(re) && objectToString(re) === '[object RegExp]';
485 }
486 exports.isRegExp = isRegExp;
487
488 function isObject(arg) {
489   return typeof arg === 'object' && arg !== null;
490 }
491 exports.isObject = isObject;
492
493 function isDate(d) {
494   return isObject(d) && objectToString(d) === '[object Date]';
495 }
496 exports.isDate = isDate;
497
498 function isError(e) {
499   return isObject(e) && objectToString(e) === '[object Error]';
500 }
501 exports.isError = isError;
502
503 function isFunction(arg) {
504   return typeof arg === 'function';
505 }
506 exports.isFunction = isFunction;
507
508 function isPrimitive(arg) {
509   return arg === null ||
510          typeof arg === 'boolean' ||
511          typeof arg === 'number' ||
512          typeof arg === 'string' ||
513          typeof arg === 'symbol' ||  // ES6 symbol
514          typeof arg === 'undefined';
515 }
516 exports.isPrimitive = isPrimitive;
517
518 function isBuffer(arg) {
519   return arg instanceof Buffer;
520 }
521 exports.isBuffer = isBuffer;
522
523 function objectToString(o) {
524   return Object.prototype.toString.call(o);
525 }
526
527
528 function pad(n) {
529   return n < 10 ? '0' + n.toString(10) : n.toString(10);
530 }
531
532
533 var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
534               'Oct', 'Nov', 'Dec'];
535
536 // 26 Feb 16:19:34
537 function timestamp() {
538   var d = new Date();
539   var time = [pad(d.getHours()),
540               pad(d.getMinutes()),
541               pad(d.getSeconds())].join(':');
542   return [d.getDate(), months[d.getMonth()], time].join(' ');
543 }
544
545
546 // log is just a thin wrapper to console.log that prepends a timestamp
547 exports.log = function() {
548   console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
549 };
550
551
552 /**
553  * Inherit the prototype methods from one constructor into another.
554  *
555  * The Function.prototype.inherits from lang.js rewritten as a standalone
556  * function (not on Function.prototype). NOTE: If this file is to be loaded
557  * during bootstrapping this function needs to be rewritten using some native
558  * functions as prototype setup using normal JavaScript does not work as
559  * expected during bootstrapping (see mirror.js in r114903).
560  *
561  * @param {function} ctor Constructor function which needs to inherit the
562  *     prototype.
563  * @param {function} superCtor Constructor function to inherit prototype from.
564  */
565 exports.inherits = function(ctor, superCtor) {
566   ctor.super_ = superCtor;
567   ctor.prototype = Object.create(superCtor.prototype, {
568     constructor: {
569       value: ctor,
570       enumerable: false,
571       writable: true,
572       configurable: true
573     }
574   });
575 };
576
577 exports._extend = function(origin, add) {
578   // Don't do anything if add isn't an object
579   if (!add || !isObject(add)) return origin;
580
581   var keys = Object.keys(add);
582   var i = keys.length;
583   while (i--) {
584     origin[keys[i]] = add[keys[i]];
585   }
586   return origin;
587 };
588
589 function hasOwnProperty(obj, prop) {
590   return Object.prototype.hasOwnProperty.call(obj, prop);
591 }
592
593
594 // Deprecated old stuff.
595
596 exports.p = exports.deprecate(function() {
597   for (var i = 0, len = arguments.length; i < len; ++i) {
598     console.error(exports.inspect(arguments[i]));
599   }
600 }, 'util.p: Use console.error() instead');
601
602
603 exports.exec = exports.deprecate(function() {
604   return require('child_process').exec.apply(this, arguments);
605 }, 'util.exec is now called `child_process.exec`.');
606
607
608 exports.print = exports.deprecate(function() {
609   for (var i = 0, len = arguments.length; i < len; ++i) {
610     process.stdout.write(String(arguments[i]));
611   }
612 }, 'util.print: Use console.log instead');
613
614
615 exports.puts = exports.deprecate(function() {
616   for (var i = 0, len = arguments.length; i < len; ++i) {
617     process.stdout.write(arguments[i] + '\n');
618   }
619 }, 'util.puts: Use console.log instead');
620
621
622 exports.debug = exports.deprecate(function(x) {
623   process.stderr.write('DEBUG: ' + x + '\n');
624 }, 'util.debug: Use console.error instead');
625
626
627 exports.error = exports.deprecate(function(x) {
628   for (var i = 0, len = arguments.length; i < len; ++i) {
629     process.stderr.write(arguments[i] + '\n');
630   }
631 }, 'util.error: Use console.error instead');
632
633
634 exports.pump = exports.deprecate(function(readStream, writeStream, callback) {
635   var callbackCalled = false;
636
637   function call(a, b, c) {
638     if (callback && !callbackCalled) {
639       callback(a, b, c);
640       callbackCalled = true;
641     }
642   }
643
644   readStream.addListener('data', function(chunk) {
645     if (writeStream.write(chunk) === false) readStream.pause();
646   });
647
648   writeStream.addListener('drain', function() {
649     readStream.resume();
650   });
651
652   readStream.addListener('end', function() {
653     writeStream.end();
654   });
655
656   readStream.addListener('close', function() {
657     call();
658   });
659
660   readStream.addListener('error', function(err) {
661     writeStream.end();
662     call(err);
663   });
664
665   writeStream.addListener('error', function(err) {
666     readStream.destroy();
667     call(err);
668   });
669 }, 'util.pump(): Use readableStream.pipe() instead');
670
671
672 var uv;
673 exports._errnoException = function(err, syscall) {
674   if (isUndefined(uv)) uv = process.binding('uv');
675   var errname = uv.errname(err);
676   var e = new Error(syscall + ' ' + errname);
677   e.code = errname;
678   e.errno = errname;
679   e.syscall = syscall;
680   return e;
681 };