improve dumper module and global dump function (#811)

- Fixed an issue in the dumper module that caused non-circular references between objects to be treated as circular references. For example, in an array of objects A and B, if B.a refers to A, the value of B.a can now be dumped.
- The dumper module is now much faster when the built-in class `Set` is not available.
- The global dump function is now much faster and is less likely to dump the value `<CircularRef>`.
This commit is contained in:
Jeff Williams 2014-11-27 10:03:26 -08:00
parent f302234811
commit 0daeb2e947
4 changed files with 52 additions and 24 deletions

View File

@ -136,10 +136,10 @@ global.app = {
global.dump = function() {
'use strict';
var doop = require('./lib/jsdoc/util/doop').doop;
var _dump = require('./lib/jsdoc/util/dumper').dump;
for (var i = 0, l = arguments.length; i < l; i++) {
console.log( _dump(doop(arguments[i])) );
console.log( _dump(arguments[i]) );
}
};

View File

@ -26,7 +26,8 @@ function hasItem(set, item) {
}
}
// TODO: should share code with jsdoc/util/dumper~ObjectWalker
// TODO: can we remove the circular-ref checking? pretty sure it's not needed anymore...
// if we need this here for some reason I'm forgetting, we should share code with jsdoc/util/dumper
function doop(o, seen) {
var clone;
var descriptor;

View File

@ -8,42 +8,37 @@
'use strict';
var util = require('util');
var setDefined = typeof Set !== 'undefined';
var OBJECT_WALKER_KEY = 'hasBeenSeenByWalkerDumper';
var SET_DEFINED = (typeof Set !== 'undefined');
function ObjectWalker() {
if (setDefined) {
this.seenItems = new Set();
} else {
this.seenItems = [];
}
this.seenItems = SET_DEFINED ? new Set() : [];
}
ObjectWalker.prototype.seen = function(object) {
var result;
if (setDefined) {
if (SET_DEFINED) {
result = this.seenItems.has(object);
} else {
result = object.hasBeenSeenByWalkerDumper;
result = object[OBJECT_WALKER_KEY];
}
return result;
};
ObjectWalker.prototype.markAsSeen = function(object) {
if (setDefined) {
if (SET_DEFINED) {
this.seenItems.add(object);
} else {
object.hasBeenSeenByWalkerDumper = true;
object[OBJECT_WALKER_KEY] = true;
this.seenItems.push(object);
}
};
ObjectWalker.prototype.cleanSeenFlag = function() {
if (setDefined) {
this.seenItems = new Set();
} else {
this.seenItems.forEach(function(object) {
delete object.hasBeenSeenByWalkerDumper;
});
ObjectWalker.prototype.removeSeenFlag = function(obj) {
if (!SET_DEFINED) {
delete obj[OBJECT_WALKER_KEY];
}
};
@ -85,10 +80,13 @@ ObjectWalker.prototype.walk = function(o) {
else if ( Array.isArray(o) ) {
result = this.checkCircularRefs(o, function(arr) {
var newArray = [];
arr.forEach(function(item) {
newArray.push( self.walk(item) );
});
self.removeSeenFlag(arr);
return newArray;
});
}
@ -107,11 +105,14 @@ ObjectWalker.prototype.walk = function(o) {
else if ( this.isObject(o) && o !== null ) {
result = this.checkCircularRefs(o, function(obj) {
var newObj = {};
Object.keys(obj).forEach(function(key) {
if (!setDefined && key === 'hasBeenSeenByWalkerDumper') { return; }
if (!SET_DEFINED && key === OBJECT_WALKER_KEY) { return; }
newObj[key] = self.walk(obj[key]);
});
self.removeSeenFlag(obj);
return newObj;
});
}
@ -128,8 +129,6 @@ ObjectWalker.prototype.walk = function(o) {
*/
exports.dump = function(object) {
var walker = new ObjectWalker();
var result = JSON.stringify(walker.walk(object), null, 4);
walker.cleanSeenFlag();
return result;
return JSON.stringify(walker.walk(object), null, 4);
};

View File

@ -140,5 +140,33 @@ describe('jsdoc/util/dumper', function() {
expect(actual).toBe(expected);
});
it('should not treat references between different objects as circular refs', function() {
var a = [
{
b: {
c: 1
}
}
];
a[1] = { d: a[0].b };
var actual = dumper.dump(a);
var expected = '' +
'[\n' +
' {\n' +
' "b": {\n' +
' "c": 1\n' +
' }\n' +
' },\n' +
' {\n' +
' "d": {\n' +
' "c": 1\n' +
' }\n' +
' }\n' +
']';
expect(actual).toBe(expected);
});
});
});