Skip to content

Commit c255575

Browse files
Uzlopaktargos
authored andcommitted
errors: improve performance of determine-specific-type
PR-URL: #49696 Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Moshe Atlow <[email protected]>
1 parent 5131fde commit c255575

File tree

5 files changed

+167
-17
lines changed

5 files changed

+167
-17
lines changed
+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
5+
const bench = common.createBenchmark(main, {
6+
n: [1e6],
7+
v: [
8+
'() => 1n',
9+
'() => true',
10+
'() => false',
11+
'() => 2',
12+
'() => +0',
13+
'() => -0',
14+
'() => NaN',
15+
'() => Infinity',
16+
'() => ""',
17+
'() => "\'"',
18+
'() => Symbol("foo")',
19+
'() => function foo() {}',
20+
'() => null',
21+
'() => undefined',
22+
'() => new Array()',
23+
'() => new BigInt64Array()',
24+
'() => new BigUint64Array()',
25+
'() => new Int8Array()',
26+
'() => new Int16Array()',
27+
'() => new Int32Array()',
28+
'() => new Float32Array()',
29+
'() => new Float64Array()',
30+
'() => new Uint8Array()',
31+
'() => new Uint8ClampedArray()',
32+
'() => new Uint16Array()',
33+
'() => new Uint32Array()',
34+
'() => new Date()',
35+
'() => new Map()',
36+
'() => new WeakMap()',
37+
'() => new Object()',
38+
'() => Promise.resolve("foo")',
39+
'() => new Set()',
40+
'() => new WeakSet()',
41+
'() => ({ __proto__: null })',
42+
],
43+
}, {
44+
flags: ['--expose-internals'],
45+
});
46+
47+
function main({ n, v }) {
48+
const {
49+
determineSpecificType,
50+
} = require('internal/errors');
51+
52+
const value = eval(v)();
53+
54+
bench.start();
55+
for (let i = 0; i < n; ++i)
56+
determineSpecificType(value);
57+
bench.end(n);
58+
}

lib/internal/errors.js

+46-15
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ const {
4848
String,
4949
StringPrototypeEndsWith,
5050
StringPrototypeIncludes,
51+
StringPrototypeIndexOf,
5152
StringPrototypeSlice,
5253
StringPrototypeSplit,
5354
StringPrototypeStartsWith,
@@ -939,23 +940,53 @@ const genericNodeError = hideStackFrames(function genericNodeError(message, erro
939940
* @returns {string}
940941
*/
941942
function determineSpecificType(value) {
942-
if (value == null) {
943-
return '' + value;
943+
if (value === null) {
944+
return 'null';
945+
} else if (value === undefined) {
946+
return 'undefined';
944947
}
945-
if (typeof value === 'function' && value.name) {
946-
return `function ${value.name}`;
947-
}
948-
if (typeof value === 'object') {
949-
if (value.constructor?.name) {
950-
return `an instance of ${value.constructor.name}`;
951-
}
952-
return `${lazyInternalUtilInspect().inspect(value, { depth: -1 })}`;
953-
}
954-
let inspected = lazyInternalUtilInspect()
955-
.inspect(value, { colors: false });
956-
if (inspected.length > 28) { inspected = `${StringPrototypeSlice(inspected, 0, 25)}...`; }
957948

958-
return `type ${typeof value} (${inspected})`;
949+
const type = typeof value;
950+
951+
switch (type) {
952+
case 'bigint':
953+
return `type bigint (${value}n)`;
954+
case 'number':
955+
if (value === 0) {
956+
return 1 / value === -Infinity ? 'type number (-0)' : 'type number (0)';
957+
} else if (value !== value) { // eslint-disable-line no-self-compare
958+
return 'type number (NaN)';
959+
} else if (value === Infinity) {
960+
return 'type number (Infinity)';
961+
} else if (value === -Infinity) {
962+
return 'type number (-Infinity)';
963+
}
964+
return `type number (${value})`;
965+
case 'boolean':
966+
return value ? 'type boolean (true)' : 'type boolean (false)';
967+
case 'symbol':
968+
return `type symbol (${String(value)})`;
969+
case 'function':
970+
return `function ${value.name}`;
971+
case 'object':
972+
if (value.constructor && 'name' in value.constructor) {
973+
return `an instance of ${value.constructor.name}`;
974+
}
975+
return `${lazyInternalUtilInspect().inspect(value, { depth: -1 })}`;
976+
case 'string':
977+
value.length > 28 && (value = `${StringPrototypeSlice(value, 0, 25)}...`);
978+
if (StringPrototypeIndexOf(value, "'") === -1) {
979+
return `type string ('${value}')`;
980+
}
981+
return `type string (${JSONStringify(value)})`;
982+
default:
983+
value = lazyInternalUtilInspect().inspect(value, { colors: false });
984+
if (value.length > 28) {
985+
value = `${StringPrototypeSlice(value, 0, 25)}...`;
986+
}
987+
988+
return `type ${type} (${value})`;
989+
}
959990
}
960991

961992
/**

test/common/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -808,7 +808,7 @@ function invalidArgTypeHelper(input) {
808808
if (input == null) {
809809
return ` Received ${input}`;
810810
}
811-
if (typeof input === 'function' && input.name) {
811+
if (typeof input === 'function') {
812812
return ` Received function ${input.name}`;
813813
}
814814
if (typeof input === 'object') {

test/parallel/test-error-value-type-detection.mjs

+61
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ strictEqual(
1212
'type bigint (1n)',
1313
);
1414

15+
strictEqual(
16+
determineSpecificType(true),
17+
'type boolean (true)',
18+
);
1519
strictEqual(
1620
determineSpecificType(false),
1721
'type boolean (false)',
@@ -42,6 +46,27 @@ strictEqual(
4246
"type string ('')",
4347
);
4448

49+
strictEqual(
50+
determineSpecificType(''),
51+
"type string ('')",
52+
);
53+
strictEqual(
54+
determineSpecificType("''"),
55+
"type string (\"''\")",
56+
);
57+
strictEqual(
58+
determineSpecificType('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor'),
59+
"type string ('Lorem ipsum dolor sit ame...')",
60+
);
61+
strictEqual(
62+
determineSpecificType("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor'"),
63+
"type string ('Lorem ipsum dolor sit ame...')",
64+
);
65+
strictEqual(
66+
determineSpecificType("Lorem' ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor"),
67+
"type string (\"Lorem' ipsum dolor sit am...\")",
68+
);
69+
4570
strictEqual(
4671
determineSpecificType(Symbol('foo')),
4772
'type symbol (Symbol(foo))',
@@ -52,6 +77,38 @@ strictEqual(
5277
'function foo',
5378
);
5479

80+
const implicitlyNamed = function() {}; // eslint-disable-line func-style
81+
strictEqual(
82+
determineSpecificType(implicitlyNamed),
83+
'function implicitlyNamed',
84+
);
85+
strictEqual(
86+
determineSpecificType(() => {}),
87+
'function ',
88+
);
89+
function noName() {}
90+
delete noName.name;
91+
strictEqual(
92+
noName.name,
93+
'',
94+
);
95+
strictEqual(
96+
determineSpecificType(noName),
97+
'function ',
98+
);
99+
100+
function * generatorFn() {}
101+
strictEqual(
102+
determineSpecificType(generatorFn),
103+
'function generatorFn',
104+
);
105+
106+
async function asyncFn() {}
107+
strictEqual(
108+
determineSpecificType(asyncFn),
109+
'function asyncFn',
110+
);
111+
55112
strictEqual(
56113
determineSpecificType(null),
57114
'null',
@@ -134,6 +191,10 @@ strictEqual(
134191
determineSpecificType({}),
135192
'an instance of Object',
136193
);
194+
strictEqual(
195+
determineSpecificType(new Object()),
196+
'an instance of Object',
197+
);
137198

138199
strictEqual(
139200
determineSpecificType(Promise.resolve('foo')),

test/parallel/test-fs-readfile-error.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ assert.throws(
6161
{
6262
code: 'ERR_INVALID_ARG_TYPE',
6363
message: 'The "path" argument must be of type string or an instance of ' +
64-
'Buffer or URL. Received type function ([Function (anonymous)])',
64+
'Buffer or URL. Received function ',
6565
name: 'TypeError'
6666
}
6767
);

0 commit comments

Comments
 (0)