diff --git a/lib/utils.js b/lib/utils.js index fc7271d019..36607c788e 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -138,7 +138,7 @@ function emptyRepresentation(value, typeHint) { * canonicalType(global) // 'global' * canonicalType(new String('foo') // 'object' * canonicalType(async function() {}) // 'asyncfunction' - * canonicalType(await import(name)) // 'module' + * canonicalType(Object.create(null)) // 'null-prototype' */ var canonicalType = (exports.canonicalType = function canonicalType(value) { if (value === undefined) { @@ -147,7 +147,10 @@ var canonicalType = (exports.canonicalType = function canonicalType(value) { return 'null'; } else if (Buffer.isBuffer(value)) { return 'buffer'; + } else if (Object.getPrototypeOf(value) === null) { + return 'null-prototype'; } + return Object.prototype.toString .call(value) .replace(/^\[.+\s(.+?)]$/, '$1') @@ -213,7 +216,7 @@ exports.type = function type(value) { exports.stringify = function (value) { var typeHint = canonicalType(value); - if (!~['object', 'array', 'function'].indexOf(typeHint)) { + if (!~['object', 'array', 'function', 'null-prototype'].indexOf(typeHint)) { if (typeHint === 'buffer') { var json = Buffer.prototype.toJSON.call(value); // Based on the toJSON result @@ -399,8 +402,12 @@ exports.canonicalize = function canonicalize(value, stack, typeHint) { break; } /* falls through */ + case 'null-prototype': case 'object': canonicalizedObj = canonicalizedObj || {}; + if (typeHint === 'null-prototype' && Symbol.toStringTag in value) { + canonicalizedObj['[Symbol.toStringTag]'] = value[Symbol.toStringTag]; + } withStack(value, function () { Object.keys(value) .sort() diff --git a/test/unit/utils.spec.js b/test/unit/utils.spec.js index 46f27b57ad..0e54bf394e 100644 --- a/test/unit/utils.spec.js +++ b/test/unit/utils.spec.js @@ -288,6 +288,44 @@ describe('lib/utils', function () { ].join('\n'); expect(stringify(expected), 'to be', actual); }); + + describe('should represent null prototypes', function () { + it('With explicit names', function () { + const foo = Object.create(null, { + [Symbol.toStringTag]: {value: 'Foo'}, + bing: {get: () => 'bong', enumerable: true} + }); + const expected = [ + '{', + ' "[Symbol.toStringTag]": "Foo"', + ' "bing": "bong"', + '}' + ].join('\n'); + + expect(stringify(foo), 'to be', expected); + }); + + it('Without names', function () { + const unnamed = { + bing: 'bong', + abc: 123 + }; + unnamed.self = unnamed; + const expected = [ + '{', + ' "abc": 123', + ' "bing": "bong"', + ' "self": [Circular]', + '}' + ].join('\n'); + + expect( + stringify(Object.setPrototypeOf(unnamed, null)), + 'to be', + expected + ); + }); + }); }); it('should canonicalize the object', function () {