◐ Shell
clean mode source ↗

util: do not rely on mutable `Object` and `Function`' `constructor` prop · nodejs/node@9e98e86

@@ -22,8 +22,11 @@ const {

2222

DatePrototypeToISOString,

2323

DatePrototypeToString,

2424

ErrorPrototypeToString,

25+

Function,

26+

FunctionPrototype,

2527

FunctionPrototypeBind,

2628

FunctionPrototypeCall,

29+

FunctionPrototypeSymbolHasInstance,

2730

FunctionPrototypeToString,

2831

JSONStringify,

2932

MapPrototypeEntries,

@@ -50,6 +53,7 @@ const {

5053

ObjectGetPrototypeOf,

5154

ObjectIs,

5255

ObjectKeys,

56+

ObjectPrototype,

5357

ObjectPrototypeHasOwnProperty,

5458

ObjectPrototypePropertyIsEnumerable,

5559

ObjectSeal,

@@ -593,10 +597,26 @@ function isInstanceof(object, proto) {

593597

}

594598

}

595599600+

// Special-case for some builtin prototypes in case their `constructor` property has been tampered.

601+

const wellKnownPrototypes = new SafeMap();

602+

wellKnownPrototypes.set(ObjectPrototype, { name: 'Object', constructor: Object });

603+

wellKnownPrototypes.set(FunctionPrototype, { name: 'Function', constructor: Function });

604+596605

function getConstructorName(obj, ctx, recurseTimes, protoProps) {

597606

let firstProto;

598607

const tmp = obj;

599608

while (obj || isUndetectableObject(obj)) {

609+

const wellKnownPrototypeNameAndConstructor = wellKnownPrototypes.get(obj);

610+

if (wellKnownPrototypeNameAndConstructor != null) {

611+

const { name, constructor } = wellKnownPrototypeNameAndConstructor;

612+

if (FunctionPrototypeSymbolHasInstance(constructor, tmp)) {

613+

if (protoProps !== undefined && firstProto !== obj) {

614+

addPrototypeProperties(

615+

ctx, tmp, firstProto || tmp, recurseTimes, protoProps);

616+

}

617+

return name;

618+

}

619+

}

600620

const descriptor = ObjectGetOwnPropertyDescriptor(obj, 'constructor');

601621

if (descriptor !== undefined &&

602622

typeof descriptor.value === 'function' &&

@@ -954,7 +974,11 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {

954974

if (noIterator) {

955975

keys = getKeys(value, ctx.showHidden);

956976

braces = ['{', '}'];

957-

if (constructor === 'Object') {

977+

if (typeof value === 'function') {

978+

base = getFunctionBase(value, constructor, tag);

979+

if (keys.length === 0 && protoProps === undefined)

980+

return ctx.stylize(base, 'special');

981+

} else if (constructor === 'Object') {

958982

if (isArgumentsObject(value)) {

959983

braces[0] = '[Arguments] {';

960984

} else if (tag !== '') {

@@ -963,10 +987,6 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {

963987

if (keys.length === 0 && protoProps === undefined) {

964988

return `${braces[0]}}`;

965989

}

966-

} else if (typeof value === 'function') {

967-

base = getFunctionBase(value, constructor, tag);

968-

if (keys.length === 0 && protoProps === undefined)

969-

return ctx.stylize(base, 'special');

970990

} else if (isRegExp(value)) {

971991

// Make RegExps say that they are RegExps

972992

base = RegExpPrototypeToString(