Merge branch '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       name = ctx.stylize(name, 'string');
418     }
419   }
420
421   return name + ': ' + str;
422 }
423
424
425 function reduceToSingleString(output, base, braces) {
426   var numLinesEst = 0;
427   var length = output.reduce(function(prev, cur) {
428     numLinesEst++;
429     if (cur.indexOf('\n') >= 0) numLinesEst++;
430     return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
431   }, 0);
432
433   if (length > 60) {
434     return braces[0] +
435            (base === '' ? '' : base + '\n ') +
436            ' ' +
437            output.join(',\n  ') +
438            ' ' +
439            braces[1];
440   }
441
442   return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
443 }
444
445
446 // NOTE: These type checking functions intentionally don't use `instanceof`
447 // because it is fragile and can be easily faked with `Object.create()`.
448 function isArray(ar) {
449   return Array.isArray(ar);
450 }
451 exports.isArray = isArray;
452
453 function isBoolean(arg) {
454   return typeof arg === 'boolean';
455 }
456 exports.isBoolean = isBoolean;
457
458 function isNull(arg) {
459   return arg === null;
460 }
461 exports.isNull = isNull;
462
463 function isNullOrUndefined(arg) {
464   return arg == null;
465 }
466 exports.isNullOrUndefined = isNullOrUndefined;
467
468 function isNumber(arg) {
469   return typeof arg === 'number';
470 }
471 exports.isNumber = isNumber;
472
473 function isString(arg) {
474   return typeof arg === 'string';
475 }
476 exports.isString = isString;
477
478 function isSymbol(arg) {
479   return typeof arg === 'symbol';
480 }
481 exports.isSymbol = isSymbol;
482
483 function isUndefined(arg) {
484   return arg === void 0;
485 }
486 exports.isUndefined = isUndefined;
487
488 function isRegExp(re) {
489   return isObject(re) && objectToString(re) === '[object RegExp]';
490 }
491 exports.isRegExp = isRegExp;
492
493 function isObject(arg) {
494   return typeof arg === 'object' && arg !== null;
495 }
496 exports.isObject = isObject;
497
498 function isDate(d) {
499   return isObject(d) && objectToString(d) === '[object Date]';
500 }
501 exports.isDate = isDate;
502
503 function isError(e) {
504   return isObject(e) &&
505       (objectToString(e) === '[object Error]' || e instanceof Error);
506 }
507 exports.isError = isError;
508
509 function isFunction(arg) {
510   return typeof arg === 'function';
511 }
512 exports.isFunction = isFunction;
513
514 function isPrimitive(arg) {
515   return arg === null ||
516          typeof arg === 'boolean' ||
517          typeof arg === 'number' ||
518          typeof arg === 'string' ||
519          typeof arg === 'symbol' ||  // ES6 symbol
520          typeof arg === 'undefined';
521 }
522 exports.isPrimitive = isPrimitive;
523
524 function isBuffer(arg) {
525   return arg instanceof Buffer;
526 }
527 exports.isBuffer = isBuffer;
528
529 function objectToString(o) {
530   return Object.prototype.toString.call(o);
531 }
532
533
534 function pad(n) {
535   return n < 10 ? '0' + n.toString(10) : n.toString(10);
536 }
537
538
539 var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
540               'Oct', 'Nov', 'Dec'];
541
542 // 26 Feb 16:19:34
543 function timestamp() {
544   var d = new Date();
545   var time = [pad(d.getHours()),
546               pad(d.getMinutes()),
547               pad(d.getSeconds())].join(':');
548   return [d.getDate(), months[d.getMonth()], time].join(' ');
549 }
550
551
552 // log is just a thin wrapper to console.log that prepends a timestamp
553 exports.log = function() {
554   console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
555 };
556
557
558 /**
559  * Inherit the prototype methods from one constructor into another.
560  *
561  * The Function.prototype.inherits from lang.js rewritten as a standalone
562  * function (not on Function.prototype). NOTE: If this file is to be loaded
563  * during bootstrapping this function needs to be rewritten using some native
564  * functions as prototype setup using normal JavaScript does not work as
565  * expected during bootstrapping (see mirror.js in r114903).
566  *
567  * @param {function} ctor Constructor function which needs to inherit the
568  *     prototype.
569  * @param {function} superCtor Constructor function to inherit prototype from.
570  */
571 exports.inherits = function(ctor, superCtor) {
572   ctor.super_ = superCtor;
573   ctor.prototype = Object.create(superCtor.prototype, {
574     constructor: {
575       value: ctor,
576       enumerable: false,
577       writable: true,
578       configurable: true
579     }
580   });
581 };
582
583 exports._extend = function(origin, add) {
584   // Don't do anything if add isn't an object
585   if (!add || !isObject(add)) return origin;
586
587   var keys = Object.keys(add);
588   var i = keys.length;
589   while (i--) {
590     origin[keys[i]] = add[keys[i]];
591   }
592   return origin;
593 };
594
595 function hasOwnProperty(obj, prop) {
596   return Object.prototype.hasOwnProperty.call(obj, prop);
597 }
598
599
600 // Deprecated old stuff.
601
602 exports.p = exports.deprecate(function() {
603   for (var i = 0, len = arguments.length; i < len; ++i) {
604     console.error(exports.inspect(arguments[i]));
605   }
606 }, 'util.p: Use console.error() instead');
607
608
609 exports.exec = exports.deprecate(function() {
610   return require('child_process').exec.apply(this, arguments);
611 }, 'util.exec is now called `child_process.exec`.');
612
613
614 exports.print = exports.deprecate(function() {
615   for (var i = 0, len = arguments.length; i < len; ++i) {
616     process.stdout.write(String(arguments[i]));
617   }
618 }, 'util.print: Use console.log instead');
619
620
621 exports.puts = exports.deprecate(function() {
622   for (var i = 0, len = arguments.length; i < len; ++i) {
623     process.stdout.write(arguments[i] + '\n');
624   }
625 }, 'util.puts: Use console.log instead');
626
627
628 exports.debug = exports.deprecate(function(x) {
629   process.stderr.write('DEBUG: ' + x + '\n');
630 }, 'util.debug: Use console.error instead');
631
632
633 exports.error = exports.deprecate(function(x) {
634   for (var i = 0, len = arguments.length; i < len; ++i) {
635     process.stderr.write(arguments[i] + '\n');
636   }
637 }, 'util.error: Use console.error instead');
638
639
640 exports.pump = exports.deprecate(function(readStream, writeStream, callback) {
641   var callbackCalled = false;
642
643   function call(a, b, c) {
644     if (callback && !callbackCalled) {
645       callback(a, b, c);
646       callbackCalled = true;
647     }
648   }
649
650   readStream.addListener('data', function(chunk) {
651     if (writeStream.write(chunk) === false) readStream.pause();
652   });
653
654   writeStream.addListener('drain', function() {
655     readStream.resume();
656   });
657
658   readStream.addListener('end', function() {
659     writeStream.end();
660   });
661
662   readStream.addListener('close', function() {
663     call();
664   });
665
666   readStream.addListener('error', function(err) {
667     writeStream.end();
668     call(err);
669   });
670
671   writeStream.addListener('error', function(err) {
672     readStream.destroy();
673     call(err);
674   });
675 }, 'util.pump(): Use readableStream.pipe() instead');
676
677
678 var uv;
679 exports._errnoException = function(err, syscall) {
680   if (isUndefined(uv)) uv = process.binding('uv');
681   var errname = uv.errname(err);
682   var e = new Error(syscall + ' ' + errname);
683   e.code = errname;
684   e.errno = errname;
685   e.syscall = syscall;
686   return e;
687 };