tools: add prefer-proto rule#46083
Conversation
fixup: add support for `Object.create(null)` fixup: extend to any 1-argument Object.create call fixup: add tests
|
Review requested:
|
|
cc @aduh95 |
|
I'm in favor of having more consistency here. I have a couple of questions:
|
|
The alternative form of I am more than happy to do a more thorough audit once there's general agreement on the lint rule's philosophy (prefer syntax over API) |
cjihrig
left a comment
There was a problem hiding this comment.
LGTM, but I'm probably less opinionated about this than some other folks 😄
GeoffreyBooth
left a comment
There was a problem hiding this comment.
👍 with the change suggested in https://github.com/nodejs/node/pull/46083/files#r1060980774
eb9100e to
06b629e
Compare
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 <[email protected]> Reviewed-By: Geoffrey Booth <[email protected]> Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Mohammed Keyvanzadeh <[email protected]> Reviewed-By: Darshan Sen <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Jacob Smith <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
|
@ljharb This commit didn't land cleanly on v19.x because some branches contain the |
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 <[email protected]> Reviewed-By: Geoffrey Booth <[email protected]> Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Mohammed Keyvanzadeh <[email protected]> Reviewed-By: Darshan Sen <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Jacob Smith <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
|
Is there some explanation for why this change happened? Nothing against it, just could use some explanation in the PR description so people know why we prefer the syntax form over the function call form. |
|
@Qard sure, i'd phrase it as:
Does that suffice? |
|
Sounds reasonable, thanks! 🙂 |
fixup: add support for `Object.create(null)` fixup: extend to any 1-argument Object.create call fixup: add tests PR-URL: #46083 Backport-PR-URL: #46239 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Geoffrey Booth <[email protected]> Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Mohammed Keyvanzadeh <[email protected]> Reviewed-By: Darshan Sen <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Jacob Smith <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
|
@ljharb about this:
I have very mixed results across gjs, bun, or node ... not the best benchmark ever, but Here a test example: const {assign, create, setPrototypeOf} = Object;
class Test {
constructor(i) {
this.i = i;
}
}
class CEWValue {
constructor(value) {
this.value = value;
}
get configurable() { return true }
get enumerable() { return true }
get writable() { return true }
}
class Null {}
setPrototypeOf(
Null.prototype,
null
);
const bench = {
["__proto__"](times, withField) {
const instances = [];
for (let {prototype} = Test, i = 0; i < times; i++)
instances[i] = withField ?
{__proto__: prototype, i: Math.random()} :
{__proto__: prototype};
return instances;
},
create(times, withField) {
const instances = [];
for (let {prototype} = Test, i = 0; i < times; i++)
instances[i] = withField ?
create(prototype, {"i": new CEWValue(Math.random())}) :
create(prototype);
return instances;
},
new(times, withField) {
const instances = [];
for (let i = 0; i < times; i++)
instances[i] = withField ?
new Test(Math.random()) :
new Test;
return instances;
},
Null(times, withField) {
const instances = [];
for (let i = 0; i < times; i++)
instances[i] = withField ?
assign(new Null, {i: Math.random()}) :
new Null;
return instances;
}
};
function test(name, times, Class = Test) {
if (typeof process === 'object')
console.log(`\x1b[1m${name}\x1b[0m`);
else
console.log(`%c${name}`, 'font-weight:bold');
let instances = [new Class(0)];
console.time('cold - no fields');
instances = bench[name](times, false);
console.timeEnd('cold - no fields');
for (let i = 0; i < 5; i++)
instances = bench[name](times, false);
console.time('hot - no fields');
instances = bench[name](times, false);
console.timeEnd('hot - no fields');
console.time('cold ? fields');
instances = bench[name](times, true);
console.timeEnd('cold ? fields');
for (let i = 0; i < 5; i++)
instances = bench[name](times, true);
console.time('hot - fields');
instances = bench[name](times, true);
console.timeEnd('hot - fields');
verify(name, instances, Class);
console.log('');
}
function verify(name, instances, Class) {
console.assert(
instances.every(o => o instanceof Class && typeof o.i === 'number'),
`${name} failed`
);
}
function benchAll(times) {
test("new", times);
test("Null", times, Null);
test("create", times);
test("__proto__", times);
}
benchAll(10_000); |
|
@WebReflection if |
|
@ljharb it's |
|
This does not land cleanly on v18.x |
|
@juanarbol thanks, does #46239 land cleanly? if not, ill try to open a backport PR. |
|
Patch to benchmark: - create(prototype, {"i": new CEWValue(Math.random())})
+ assign(create(prototype), {"i": Math.random()}) :
after patch
const {assign, create, setPrototypeOf} = Object;
class Test {
constructor(i) {
this.i = i;
}
}
class CEWValue {
constructor(value) {
this.value = value;
}
get configurable() { return true }
get enumerable() { return true }
get writable() { return true }
}
class Null {}
setPrototypeOf(
Null.prototype,
null
);
const bench = {
["__proto__"](times, withField) {
const instances = [];
for (let {prototype} = Test, i = 0; i < times; i++)
instances[i] = withField ?
{__proto__: prototype, i: Math.random()} :
{__proto__: prototype};
return instances;
},
create(times, withField) {
const instances = [];
for (let {prototype} = Test, i = 0; i < times; i++)
instances[i] = withField ?
assign(create(prototype), {"i": Math.random()}) :
create(prototype);
return instances;
},
new(times, withField) {
const instances = [];
for (let i = 0; i < times; i++)
instances[i] = withField ?
new Test(Math.random()) :
new Test;
return instances;
},
Null(times, withField) {
const instances = [];
for (let i = 0; i < times; i++)
instances[i] = withField ?
assign(new Null, {i: Math.random()}) :
new Null;
return instances;
}
};
function test(name, times, Class = Test) {
if (typeof process === 'object')
console.log(`\x1b[1m${name}\x1b[0m`);
else
console.log(`%c${name}`, 'font-weight:bold');
let instances = [new Class(0)];
console.time('cold - no fields');
instances = bench[name](times, false);
console.timeEnd('cold - no fields');
for (let i = 0; i < 5; i++)
instances = bench[name](times, false);
console.time('hot - no fields');
instances = bench[name](times, false);
console.timeEnd('hot - no fields');
console.time('cold ? fields');
instances = bench[name](times, true);
console.timeEnd('cold ? fields');
for (let i = 0; i < 5; i++)
instances = bench[name](times, true);
console.time('hot - fields');
instances = bench[name](times, true);
console.timeEnd('hot - fields');
verify(name, instances, Class);
console.log('');
}
function verify(name, instances, Class) {
console.assert(
instances.every(o => o instanceof Class && typeof o.i === 'number'),
`${name} failed`
);
}
function benchAll(times) {
test("new", times);
test("Null", times, Null);
test("create", times);
test("__proto__", times);
}
benchAll(10_000);node v19.5.0, ubuntu 20.04 |
If this lands, I'd be happy to refactor this rule to a
prefer-syntaxrule, which also checks for things likeObjectAssignwith a literal as the first argument, use ofBoolean(x)instead of!!x, etc.