Skip to content

Commit

Permalink
tools: add prefer-proto rule
Browse files Browse the repository at this point in the history
fixup: add support for `Object.create(null)`

fixup: extend to any 1-argument Object.create call

fixup: add tests
PR-URL: nodejs#46083
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Mohammed Keyvanzadeh <mohammadkeyvanzade94@gmail.com>
Reviewed-By: Darshan Sen <raisinten@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Jacob Smith <jacob@frende.me>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
  • Loading branch information
ljharb authored and RafaelGSS committed Jan 17, 2023
1 parent f2479d8 commit 87b2b44
Show file tree
Hide file tree
Showing 86 changed files with 306 additions and 229 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Expand Up @@ -314,6 +314,7 @@ module.exports = {
// Custom rules from eslint-plugin-node-core
'node-core/no-unescaped-regexp-dot': 'error',
'node-core/no-duplicate-requires': 'error',
'node-core/prefer-proto': 'error',
},
globals: {
ByteLengthQueuingStrategy: 'readable',
Expand Down
4 changes: 2 additions & 2 deletions benchmark/es/map-bench.js
Expand Up @@ -25,7 +25,7 @@ function runObject(n) {
}

function runNullProtoObject(n) {
const m = Object.create(null);
const m = { __proto__: null };
bench.start();
for (let i = 0; i < n; i++) {
m[`i${i}`] = i;
Expand All @@ -51,7 +51,7 @@ function runNullProtoLiteralObject(n) {
}

function StorageObject() {}
StorageObject.prototype = Object.create(null);
StorageObject.prototype = { __proto__: null };

function runStorageObject(n) {
const m = new StorageObject();
Expand Down
8 changes: 4 additions & 4 deletions doc/api/assert.md
Expand Up @@ -664,7 +664,7 @@ const obj3 = {
b: 1,
},
};
const obj4 = Object.create(obj1);
const obj4 = { __proto__: obj1 };

assert.deepEqual(obj1, obj1);
// OK
Expand Down Expand Up @@ -699,7 +699,7 @@ const obj3 = {
b: 1,
},
};
const obj4 = Object.create(obj1);
const obj4 = { __proto__: obj1 };

assert.deepEqual(obj1, obj1);
// OK
Expand Down Expand Up @@ -1623,7 +1623,7 @@ const obj3 = {
b: 1,
},
};
const obj4 = Object.create(obj1);
const obj4 = { __proto__: obj1 };

assert.notDeepEqual(obj1, obj1);
// AssertionError: { a: { b: 1 } } notDeepEqual { a: { b: 1 } }
Expand Down Expand Up @@ -1656,7 +1656,7 @@ const obj3 = {
b: 1,
},
};
const obj4 = Object.create(obj1);
const obj4 = { __proto__: obj1 };

assert.notDeepEqual(obj1, obj1);
// AssertionError: { a: { b: 1 } } notDeepEqual { a: { b: 1 } }
Expand Down
7 changes: 3 additions & 4 deletions lib/_http_agent.js
Expand Up @@ -32,7 +32,6 @@ const {
FunctionPrototypeCall,
NumberIsNaN,
NumberParseInt,
ObjectCreate,
ObjectKeys,
ObjectSetPrototypeOf,
ObjectValues,
Expand Down Expand Up @@ -111,9 +110,9 @@ function Agent(options) {

// Don't confuse net and make it think that we're connecting to a pipe
this.options.path = null;
this.requests = ObjectCreate(null);
this.sockets = ObjectCreate(null);
this.freeSockets = ObjectCreate(null);
this.requests = { __proto__: null };
this.sockets = { __proto__: null };
this.freeSockets = { __proto__: null };
this.keepAliveMsecs = this.options.keepAliveMsecs || 1000;
this.keepAlive = this.options.keepAlive || false;
this.maxSockets = this.options.maxSockets || Agent.defaultMaxSockets;
Expand Down
9 changes: 4 additions & 5 deletions lib/_http_outgoing.js
Expand Up @@ -28,7 +28,6 @@ const {
MathAbs,
MathFloor,
NumberPrototypeToString,
ObjectCreate,
ObjectDefineProperty,
ObjectKeys,
ObjectValues,
Expand Down Expand Up @@ -217,7 +216,7 @@ ObjectDefineProperty(OutgoingMessage.prototype, '_headers', {
if (val == null) {
this[kOutHeaders] = null;
} else if (typeof val === 'object') {
const headers = this[kOutHeaders] = ObjectCreate(null);
const headers = this[kOutHeaders] = { __proto__: null };
const keys = ObjectKeys(val);
// Retain for(;;) loop for performance reasons
// Refs: https://github.com/nodejs/node/pull/30958
Expand All @@ -244,7 +243,7 @@ ObjectDefineProperty(OutgoingMessage.prototype, '_headerNames', {
get: internalUtil.deprecate(function() {
const headers = this[kOutHeaders];
if (headers !== null) {
const out = ObjectCreate(null);
const out = { __proto__: null };
const keys = ObjectKeys(headers);
// Retain for(;;) loop for performance reasons
// Refs: https://github.com/nodejs/node/pull/30958
Expand Down Expand Up @@ -667,7 +666,7 @@ OutgoingMessage.prototype.setHeader = function setHeader(name, value) {

let headers = this[kOutHeaders];
if (headers === null)
this[kOutHeaders] = headers = ObjectCreate(null);
this[kOutHeaders] = headers = { __proto__: null };

headers[StringPrototypeToLowerCase(name)] = [name, value];
return this;
Expand Down Expand Up @@ -764,7 +763,7 @@ OutgoingMessage.prototype.getRawHeaderNames = function getRawHeaderNames() {
// Returns a shallow copy of the current outgoing headers.
OutgoingMessage.prototype.getHeaders = function getHeaders() {
const headers = this[kOutHeaders];
const ret = ObjectCreate(null);
const ret = { __proto__: null };
if (headers) {
const keys = ObjectKeys(headers);
// Retain for(;;) loop for performance reasons
Expand Down
3 changes: 1 addition & 2 deletions lib/_tls_common.js
Expand Up @@ -26,7 +26,6 @@ const tls = require('tls');
const {
ArrayPrototypePush,
JSONParse,
ObjectCreate,
RegExpPrototypeSymbolReplace,
} = primordials;

Expand Down Expand Up @@ -131,7 +130,7 @@ function translatePeerCertificate(c) {
}
if (c.infoAccess != null) {
const info = c.infoAccess;
c.infoAccess = ObjectCreate(null);
c.infoAccess = { __proto__: null };

// XXX: More key validation?
RegExpPrototypeSymbolReplace(/([^\n:]*):([^\n]*)(?:\n|$)/g, info,
Expand Down
5 changes: 2 additions & 3 deletions lib/buffer.js
Expand Up @@ -33,7 +33,6 @@ const {
NumberIsNaN,
NumberMAX_SAFE_INTEGER,
NumberMIN_SAFE_INTEGER,
ObjectCreate,
ObjectDefineProperties,
ObjectDefineProperty,
ObjectSetPrototypeOf,
Expand Down Expand Up @@ -146,7 +145,7 @@ const constants = ObjectDefineProperties({}, {
Buffer.poolSize = 8 * 1024;
let poolSize, poolOffset, allocPool;

const encodingsMap = ObjectCreate(null);
const encodingsMap = { __proto__: null };
for (let i = 0; i < encodings.length; ++i)
encodingsMap[encodings[i]] = i;

Expand Down Expand Up @@ -845,7 +844,7 @@ Buffer.prototype[customInspectSymbol] = function inspect(recurseTimes, ctx) {
if (ctx) {
let extras = false;
const filter = ctx.showHidden ? ALL_PROPERTIES : ONLY_ENUMERABLE;
const obj = ObjectCreate(null);
const obj = { __proto__: null };
ArrayPrototypeForEach(getOwnNonIndexProperties(this, filter),
(key) => {
extras = true;
Expand Down
3 changes: 1 addition & 2 deletions lib/diagnostics_channel.js
Expand Up @@ -4,7 +4,6 @@ const {
ArrayPrototypeIndexOf,
ArrayPrototypePush,
ArrayPrototypeSplice,
ObjectCreate,
ObjectGetPrototypeOf,
ObjectSetPrototypeOf,
SymbolHasInstance,
Expand Down Expand Up @@ -92,7 +91,7 @@ class Channel {
publish() {}
}

const channels = ObjectCreate(null);
const channels = { __proto__: null };

function channel(name) {
let channel;
Expand Down
13 changes: 6 additions & 7 deletions lib/events.js
Expand Up @@ -33,7 +33,6 @@ const {
FunctionPrototypeBind,
FunctionPrototypeCall,
NumberIsNaN,
ObjectCreate,
ObjectDefineProperty,
ObjectDefineProperties,
ObjectGetPrototypeOf,
Expand Down Expand Up @@ -338,7 +337,7 @@ EventEmitter.init = function(opts) {

if (this._events === undefined ||
this._events === ObjectGetPrototypeOf(this)._events) {
this._events = ObjectCreate(null);
this._events = { __proto__: null };
this._eventsCount = 0;
}

Expand Down Expand Up @@ -547,7 +546,7 @@ function _addListener(target, type, listener, prepend) {

events = target._events;
if (events === undefined) {
events = target._events = ObjectCreate(null);
events = target._events = { __proto__: null };
target._eventsCount = 0;
} else {
// To avoid recursion in the case that type === "newListener"! Before
Expand Down Expand Up @@ -685,7 +684,7 @@ EventEmitter.prototype.removeListener =

if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0)
this._events = ObjectCreate(null);
this._events = { __proto__: null };
else {
delete events[type];
if (events.removeListener)
Expand Down Expand Up @@ -740,11 +739,11 @@ EventEmitter.prototype.removeAllListeners =
// Not listening for removeListener, no need to emit
if (events.removeListener === undefined) {
if (arguments.length === 0) {
this._events = ObjectCreate(null);
this._events = { __proto__: null };
this._eventsCount = 0;
} else if (events[type] !== undefined) {
if (--this._eventsCount === 0)
this._events = ObjectCreate(null);
this._events = { __proto__: null };
else
delete events[type];
}
Expand All @@ -758,7 +757,7 @@ EventEmitter.prototype.removeAllListeners =
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = ObjectCreate(null);
this._events = { __proto__: null };
this._eventsCount = 0;
return this;
}
Expand Down
3 changes: 1 addition & 2 deletions lib/internal/assert/assertion_error.js
Expand Up @@ -6,7 +6,6 @@ const {
Error,
ErrorCaptureStackTrace,
MathMax,
ObjectCreate,
ObjectDefineProperty,
ObjectGetPrototypeOf,
ObjectKeys,
Expand Down Expand Up @@ -48,7 +47,7 @@ const kMaxShortLength = 12;

function copyError(source) {
const keys = ObjectKeys(source);
const target = ObjectCreate(ObjectGetPrototypeOf(source));
const target = { __proto__: ObjectGetPrototypeOf(source) };
for (const key of keys) {
target[key] = source[key];
}
Expand Down
5 changes: 2 additions & 3 deletions lib/internal/bootstrap/loaders.js
Expand Up @@ -49,7 +49,6 @@ const {
ArrayPrototypePush,
ArrayPrototypeSlice,
Error,
ObjectCreate,
ObjectDefineProperty,
ObjectKeys,
ObjectPrototypeHasOwnProperty,
Expand Down Expand Up @@ -129,7 +128,7 @@ const schemelessBlockList = new SafeSet([

// Set up process.binding() and process._linkedBinding().
{
const bindingObj = ObjectCreate(null);
const bindingObj = { __proto__: null };

process.binding = function binding(module) {
module = String(module);
Expand Down Expand Up @@ -167,7 +166,7 @@ const schemelessBlockList = new SafeSet([
*/
let internalBinding;
{
const bindingObj = ObjectCreate(null);
const bindingObj = { __proto__: null };
// eslint-disable-next-line no-global-assign
internalBinding = function internalBinding(module) {
let mod = bindingObj[module];
Expand Down
3 changes: 1 addition & 2 deletions lib/internal/cluster/round_robin_handle.js
Expand Up @@ -3,7 +3,6 @@
const {
ArrayIsArray,
Boolean,
ObjectCreate,
SafeMap,
} = primordials;

Expand All @@ -19,7 +18,7 @@ function RoundRobinHandle(key, address, { port, fd, flags, backlog, readableAll,
this.key = key;
this.all = new SafeMap();
this.free = new SafeMap();
this.handles = init(ObjectCreate(null));
this.handles = init({ __proto__: null });
this.handle = null;
this.server = net.createServer(assert.fail);

Expand Down
3 changes: 1 addition & 2 deletions lib/internal/console/constructor.js
Expand Up @@ -15,7 +15,6 @@ const {
MathFloor,
Number,
NumberPrototypeToFixed,
ObjectCreate,
ObjectDefineProperties,
ObjectDefineProperty,
ObjectKeys,
Expand Down Expand Up @@ -572,7 +571,7 @@ const consoleMethods = {
return final([iterKey, valuesKey], [getIndexArray(length), values]);
}

const map = ObjectCreate(null);
const map = { __proto__: null };
let hasPrimitives = false;
const valuesKeyArray = [];
const indexKeyArray = ObjectKeys(tabularData);
Expand Down
3 changes: 1 addition & 2 deletions lib/internal/console/global.js
Expand Up @@ -14,7 +14,6 @@

const {
FunctionPrototypeBind,
ObjectCreate,
ReflectDefineProperty,
ReflectGetOwnPropertyDescriptor,
ReflectOwnKeys,
Expand All @@ -24,7 +23,7 @@ const {
Console
} = require('internal/console/constructor');

const globalConsole = ObjectCreate({});
const globalConsole = { __proto__: {} };

// Since Console is not on the prototype chain of the global console,
// the symbol properties on Console.prototype have to be looked up from
Expand Down
3 changes: 1 addition & 2 deletions lib/internal/dns/utils.js
Expand Up @@ -9,7 +9,6 @@ const {
NumberParseInt,
RegExpPrototypeExec,
RegExpPrototypeSymbolReplace,
ObjectCreate,
Symbol,
} = primordials;

Expand Down Expand Up @@ -286,7 +285,7 @@ function setDefaultResultOrder(value) {
}

function createResolverClass(resolver) {
const resolveMap = ObjectCreate(null);
const resolveMap = { __proto__: null };

class Resolver extends ResolverBase {}

Expand Down
7 changes: 3 additions & 4 deletions lib/internal/encoding.js
Expand Up @@ -5,7 +5,6 @@

const {
Boolean,
ObjectCreate,
ObjectDefineProperties,
ObjectGetOwnPropertyDescriptors,
ObjectSetPrototypeOf,
Expand Down Expand Up @@ -350,9 +349,9 @@ class TextEncoder {
if (typeof depth === 'number' && depth < 0)
return this;
const ctor = getConstructorOf(this);
const obj = ObjectCreate({
const obj = { __proto__: {
constructor: ctor === null ? TextEncoder : ctor
});
} };
obj.encoding = this.encoding;
// Lazy to avoid circular dependency
return require('internal/util/inspect').inspect(obj, opts);
Expand Down Expand Up @@ -571,7 +570,7 @@ const sharedProperties = ObjectGetOwnPropertyDescriptors({
if (typeof depth === 'number' && depth < 0)
return this;
const constructor = getConstructorOf(this) || TextDecoder;
const obj = ObjectCreate({ constructor });
const obj = { __proto__: { constructor } };
obj.encoding = this.encoding;
obj.fatal = this.fatal;
obj.ignoreBOM = this.ignoreBOM;
Expand Down
2 changes: 1 addition & 1 deletion lib/internal/error_serdes.js
Expand Up @@ -33,7 +33,7 @@ const errors = {
const errorConstructorNames = new SafeSet(ObjectKeys(errors));

function TryGetAllProperties(object, target = object) {
const all = ObjectCreate(null);
const all = { __proto__: null };
if (object === null)
return all;
ObjectAssign(all,
Expand Down

0 comments on commit 87b2b44

Please sign in to comment.