diff --git a/lib/buffer.js b/lib/buffer.js index 756657e910893e..8f235e5f0dae6c 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -35,6 +35,7 @@ const { NumberMIN_SAFE_INTEGER, ObjectDefineProperties, ObjectDefineProperty, + ObjectPrototypeHasOwnProperty, ObjectSetPrototypeOf, RegExpPrototypeSymbolReplace, StringPrototypeCharCodeAt, @@ -910,7 +911,14 @@ Buffer.prototype[customInspectSymbol] = function inspect(recurseTimes, ctx) { }), 27, -2); } } - return `<${this.constructor.name} ${str}>`; + let constructorName = 'Buffer'; + try { + const { constructor } = this; + if (typeof constructor === 'function' && ObjectPrototypeHasOwnProperty(constructor, 'name')) { + constructorName = constructor.name; + } + } catch { /* Ignore error and use default name */ } + return `<${constructorName} ${str}>`; }; Buffer.prototype.inspect = Buffer.prototype[customInspectSymbol]; diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 1f1be555c96e08..57586a888e191f 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -2,7 +2,10 @@ const { Array, + ArrayBuffer, + ArrayBufferPrototype, ArrayIsArray, + ArrayPrototype, ArrayPrototypeFilter, ArrayPrototypeForEach, ArrayPrototypeIncludes, @@ -29,6 +32,8 @@ const { FunctionPrototypeSymbolHasInstance, FunctionPrototypeToString, JSONStringify, + Map, + MapPrototype, MapPrototypeEntries, MapPrototypeGetSize, MathFloor, @@ -68,6 +73,8 @@ const { SafeMap, SafeSet, SafeStringIterator, + Set, + SetPrototype, SetPrototypeGetSize, SetPrototypeValues, String, @@ -93,6 +100,8 @@ const { SymbolPrototypeValueOf, SymbolToPrimitive, SymbolToStringTag, + TypedArray, + TypedArrayPrototype, TypedArrayPrototypeGetLength, TypedArrayPrototypeGetSymbolToStringTag, Uint8Array, @@ -599,8 +608,13 @@ function isInstanceof(object, proto) { // Special-case for some builtin prototypes in case their `constructor` property has been tampered. const wellKnownPrototypes = new SafeMap(); -wellKnownPrototypes.set(ObjectPrototype, { name: 'Object', constructor: Object }); +wellKnownPrototypes.set(ArrayPrototype, { name: 'Array', constructor: Array }); +wellKnownPrototypes.set(ArrayBufferPrototype, { name: 'ArrayBuffer', constructor: ArrayBuffer }); wellKnownPrototypes.set(FunctionPrototype, { name: 'Function', constructor: Function }); +wellKnownPrototypes.set(MapPrototype, { name: 'Map', constructor: Map }); +wellKnownPrototypes.set(ObjectPrototype, { name: 'Object', constructor: Object }); +wellKnownPrototypes.set(SetPrototype, { name: 'Set', constructor: Set }); +wellKnownPrototypes.set(TypedArrayPrototype, { name: 'TypedArray', constructor: TypedArray }); function getConstructorName(obj, ctx, recurseTimes, protoProps) { let firstProto; @@ -825,12 +839,12 @@ function formatValue(ctx, value, recurseTimes, typedArray) { // Filter out the util module, its inspect function is special. maybeCustom !== inspect && // Also filter out any prototype objects using the circular check. - !(value.constructor && value.constructor.prototype === value)) { + ObjectGetOwnPropertyDescriptor(value, 'constructor')?.value?.prototype !== value) { // This makes sure the recurseTimes are reported as before while using // a counter internally. const depth = ctx.depth === null ? null : ctx.depth - recurseTimes; const isCrossContext = - proxy !== undefined || !(context instanceof Object); + proxy !== undefined || !FunctionPrototypeSymbolHasInstance(Object, context); const ret = FunctionPrototypeCall( maybeCustom, context, diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 04fc82cfc1aa80..e69c0349dbafa9 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -3353,3 +3353,44 @@ assert.strictEqual( ); Object.defineProperty(BuiltinPrototype, 'constructor', desc); } +{ + const prototypes = [ + Array.prototype, + ArrayBuffer.prototype, + Buffer.prototype, + Function.prototype, + Map.prototype, + Object.prototype, + Reflect.getPrototypeOf(Uint8Array.prototype), + Set.prototype, + Uint8Array.prototype, + ]; + const descriptors = new Map(); + const buffer = Buffer.from('Hello'); + const o = { + arrayBuffer: new ArrayBuffer(), buffer, typedArray: Uint8Array.from(buffer), + array: [], func() {}, set: new Set([1]), map: new Map(), + }; + for (const BuiltinPrototype of prototypes) { + descriptors.set(BuiltinPrototype, Reflect.getOwnPropertyDescriptor(BuiltinPrototype, 'constructor')); + Object.defineProperty(BuiltinPrototype, 'constructor', { + get: () => BuiltinPrototype, + configurable: true, + }); + } + assert.strictEqual( + util.inspect(o), + '{\n' + + ' arrayBuffer: ArrayBuffer { [Uint8Contents]: <>, byteLength: 0 },\n' + + ' buffer: ,\n' + + ' typedArray: TypedArray(5) [Uint8Array] [ 72, 101, 108, 108, 111 ],\n' + + ' array: [],\n' + + ' func: [Function: func],\n' + + ' set: Set(1) { 1 },\n' + + ' map: Map(0) {}\n' + + '}', + ); + for (const [BuiltinPrototype, desc] of descriptors) { + Object.defineProperty(BuiltinPrototype, 'constructor', desc); + } +}