diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 59c4c8a833e445..bd7a17166d0ed1 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -534,6 +534,14 @@ function getEmptyFormatArray() { return []; } +function isInstanceof(object, proto) { + try { + return object instanceof proto; + } catch { + return false; + } +} + function getConstructorName(obj, ctx, recurseTimes, protoProps) { let firstProto; const tmp = obj; @@ -542,7 +550,7 @@ function getConstructorName(obj, ctx, recurseTimes, protoProps) { if (descriptor !== undefined && typeof descriptor.value === 'function' && descriptor.value.name !== '' && - tmp instanceof descriptor.value) { + isInstanceof(tmp, descriptor.value)) { if (protoProps !== undefined && (firstProto !== obj || !builtInObjects.has(descriptor.value.name))) { @@ -644,7 +652,7 @@ function addPrototypeProperties(ctx, main, obj, recurseTimes, output) { function getPrefix(constructor, tag, fallback, size = '') { if (constructor === null) { - if (tag !== '') { + if (tag !== '' && fallback !== tag) { return `[${fallback}${size}: null prototype] [${tag}] `; } return `[${fallback}${size}: null prototype] `; @@ -978,7 +986,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { braces[0] = `${getPrefix(constructor, tag, 'WeakMap')}{`; formatter = ctx.showHidden ? formatWeakMap : formatWeakCollection; } else if (isModuleNamespaceObject(value)) { - braces[0] = `[${tag}] {`; + braces[0] = `${getPrefix(constructor, tag, 'Module')}{`; // Special handle keys for namespace objects. formatter = formatNamespaceObject.bind(null, keys); } else if (isBoxedPrimitive(value)) { diff --git a/test/es-module/test-esm-loader-custom-condition.mjs b/test/es-module/test-esm-loader-custom-condition.mjs index 26d1db4842f621..075e4fe2b66029 100644 --- a/test/es-module/test-esm-loader-custom-condition.mjs +++ b/test/es-module/test-esm-loader-custom-condition.mjs @@ -1,7 +1,21 @@ // Flags: --experimental-loader ./test/fixtures/es-module-loaders/loader-with-custom-condition.mjs import '../common/index.mjs'; import assert from 'assert'; +import util from 'util'; import * as ns from '../fixtures/es-modules/conditional-exports.mjs'; assert.deepStrictEqual({ ...ns }, { default: 'from custom condition' }); + +assert.strictEqual( + util.inspect(ns, { showHidden: false }), + "[Module: null prototype] { default: 'from custom condition' }" +); + +assert.strictEqual( + util.inspect(ns, { showHidden: true }), + '[Module: null prototype] {\n' + + " default: 'from custom condition',\n" + + " [Symbol(Symbol.toStringTag)]: 'Module'\n" + + '}' +); diff --git a/test/parallel/test-repl-import-referrer.js b/test/parallel/test-repl-import-referrer.js index 33bc442e6f69c8..d77d70a031a14a 100644 --- a/test/parallel/test-repl-import-referrer.js +++ b/test/parallel/test-repl-import-referrer.js @@ -16,7 +16,10 @@ child.stdout.on('data', (data) => { child.on('exit', common.mustCall(() => { const results = output.replace(/^> /mg, '').split('\n').slice(2); - assert.deepStrictEqual(results, ['[Module] { message: \'A message\' }', '']); + assert.deepStrictEqual( + results, + ['[Module: null prototype] { message: \'A message\' }', ''] + ); })); child.stdin.write('await import(\'./message.mjs\');\n'); diff --git a/test/parallel/test-util-inspect-namespace.js b/test/parallel/test-util-inspect-namespace.js index fc13fca6c6ac98..244166f3819830 100644 --- a/test/parallel/test-util-inspect-namespace.js +++ b/test/parallel/test-util-inspect-namespace.js @@ -11,7 +11,10 @@ const { inspect } = require('util'); await m.link(() => 0); assert.strictEqual( inspect(m.namespace), - '[Module] { a: , b: undefined }'); + '[Module: null prototype] { a: , b: undefined }'); await m.evaluate(); - assert.strictEqual(inspect(m.namespace), '[Module] { a: 1, b: 2 }'); + assert.strictEqual( + inspect(m.namespace), + '[Module: null prototype] { a: 1, b: 2 }' + ); })().then(common.mustCall()); diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 634f27690e5a59..56065beb458832 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -2866,6 +2866,17 @@ assert.strictEqual( ); } +// Check that prototypes with a null prototype are inspectable. +// Regression test for https://github.com/nodejs/node/issues/35730 +{ + function Func() {} + Func.prototype = null; + const object = {}; + object.constructor = Func; + + assert.strictEqual(util.inspect(object), '{ constructor: [Function: Func] }'); +} + // Test changing util.inspect.colors colors and aliases. { const colors = util.inspect.colors;