util: do not rely on mutable `Object` and `Function`' `constructor` prop · nodejs/node@77397c5
@@ -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+596605function getConstructorName(obj, ctx, recurseTimes, protoProps) {
597606let firstProto;
598607const tmp = obj;
599608while (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+}
600620const descriptor = ObjectGetOwnPropertyDescriptor(obj, 'constructor');
601621if (descriptor !== undefined &&
602622typeof descriptor.value === 'function' &&
@@ -954,7 +974,11 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
954974if (noIterator) {
955975keys = getKeys(value, ctx.showHidden);
956976braces = ['{', '}'];
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') {
958982if (isArgumentsObject(value)) {
959983braces[0] = '[Arguments] {';
960984} else if (tag !== '') {
@@ -963,10 +987,6 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
963987if (keys.length === 0 && protoProps === undefined) {
964988return `${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
972992base = RegExpPrototypeToString(