From 34c02357ff8e00c35e493754943f599dba0b0f29 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Tue, 1 Dec 2009 21:20:14 +1100 Subject: [PATCH] sys.inspect is totally more awesome now - No longer relies on JSON.stringify, so it can output nulls and functions - Handles circular references better - Has tests --- lib/sys.js | 84 ++++++++++++++++++++++++++++++---------- test/mjsunit/test-sys.js | 26 +++++++++++++ 2 files changed, 90 insertions(+), 20 deletions(-) create mode 100644 test/mjsunit/test-sys.js diff --git a/lib/sys.js b/lib/sys.js index f755d71638..9c4dc6d944 100644 --- a/lib/sys.js +++ b/lib/sys.js @@ -17,26 +17,11 @@ exports.error = function (x) { /** * Echos the value of a value. Trys to print the value out * in the best way possible given the different types. - * + * * @param {Object} value The object to print out */ exports.inspect = function (value) { - if (value === 0) return "0"; - if (value === false) return "false"; - if (value === "") return '""'; - if (typeof(value) == "function") return "[Function]"; - if (value === undefined) return; - - try { - return JSON.stringify(value, undefined, 1); - } catch (e) { - // TODO make this recusrive and do a partial JSON output of object. - if (e.message.search("circular")) { - return "[Circular Object]"; - } else { - throw e; - } - } + return formatter(value, '', []); }; exports.p = function (x) { @@ -50,13 +35,13 @@ exports.exec = function (command) { var promise = new process.Promise(); child.addListener("output", function (chunk) { - if (chunk) stdout += chunk; + if (chunk) stdout += chunk; }); child.addListener("error", function (chunk) { - if (chunk) stderr += chunk; + if (chunk) stderr += chunk; }); - + child.addListener("exit", function (code) { if (code == 0) { promise.emitSuccess(stdout, stderr); @@ -82,3 +67,62 @@ exports.exec = function (command) { * @param {function} superCtor Constructor function to inherit prototype from */ exports.inherits = process.inherits; + +/** + * A recursive function to format an object - used by inspect. + * + * @param {Object} value + * the value to format + * @param {String} indent + * the indent level of any nested objects, since they are formatted over + * more than one line + * @param {Array} parents + * contains all objects above the current one in the heirachy, used to + * prevent getting stuck in a loop on circular references + */ +var formatter = function(value, indent, parents) { + switch(typeof(value)) { + case 'string': return '"' + value + '"'; + case 'number': return '' + value; + case 'function': return '[Function]'; + case 'boolean': return '' + value; + case 'undefined': return 'undefined'; + case 'object': + if (value == null) return 'null'; + if (parents.indexOf(value) >= 0) return '[Circular]'; + parents.push(value); + + if (value instanceof Array) { + return formatObject(value, indent, parents, '[]', function(x, f) { + return f(value[x]); + }); + } else { + return formatObject(value, indent, parents, '{}', function(x, f) { + return f(x) + ': ' + f(value[x]); + }); + } + return buffer; + default: + throw('inspect unimplemented for ' + typeof(value)); + } +} + +/** + * Helper function for formatting either an array or an object, used internally by formatter + */ +var formatObject = function(obj, indent, parents, parenthesis, entryFormatter) { + var buffer = parenthesis[0]; + var values = []; + + var localFormatter = function(value) { + return formatter(value, indent + ' ', parents); + }; + for (x in obj) { + values.push(indent + ' ' + entryFormatter(x, localFormatter)); + } + if (values.length > 0) { + buffer += "\n" + values.join(",\n") + "\n" + indent; + } + buffer += parenthesis[1]; + return buffer; +} diff --git a/test/mjsunit/test-sys.js b/test/mjsunit/test-sys.js new file mode 100644 index 0000000000..14ce55157b --- /dev/null +++ b/test/mjsunit/test-sys.js @@ -0,0 +1,26 @@ +process.mixin(require("./common")); +process.mixin(require("sys")); + +assert.equal("0", inspect(0)); +assert.equal("1", inspect(1)); +assert.equal("false", inspect(false)); +assert.equal('""', inspect("")); +assert.equal('"hello"', inspect("hello")); +assert.equal("[Function]", inspect(function() {})); +assert.equal('undefined', inspect(undefined)); +assert.equal('null', inspect(null)); + +assert.equal('[]', inspect([])); +assert.equal('[\n 1,\n 2\n]', inspect([1, 2])); +assert.equal('[\n 1,\n [\n 2,\n 3\n ]\n]', inspect([1, [2, 3]])); + +assert.equal('{}', inspect({})); +assert.equal('{\n "a": 1\n}', inspect({a: 1})); +assert.equal('{\n "a": [Function]\n}', inspect({a: function() {}})); +assert.equal('{\n "a": 1,\n "b": 2\n}', inspect({a: 1, b: 2})); +assert.equal('{\n "a": {}\n}', inspect({'a': {}})); +assert.equal('{\n "a": {\n "b": 2\n }\n}', inspect({'a': {'b': 2}})); + +var value = {} +value['a'] = value; +assert.equal('{\n "a": [Circular]\n}', inspect(value));