Skip to content

Commit ada2d05

Browse files
authored
process: runtime deprecate coercion to integer in process.exit()
This is a follow up of doc-only deprecation #43738. Signed-off-by: Daeyeon Jeong [email protected] PR-URL: #44711 Reviewed-By: Antoine du Hamel <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]> Reviewed-By: Darshan Sen <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 24d255f commit ada2d05

File tree

6 files changed

+171
-5
lines changed

6 files changed

+171
-5
lines changed

doc/api/deprecations.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -3175,6 +3175,9 @@ thing instead.
31753175

31763176
<!-- YAML
31773177
changes:
3178+
- version: REPLACEME
3179+
pr-url: https://github.com/nodejs/node/pull/44711
3180+
description: Runtime deprecation.
31783181
- version: v18.10.0
31793182
pr-url: https://github.com/nodejs/node/pull/44714
31803183
description: Documentation-only deprecation of `process.exitCode` integer
@@ -3187,7 +3190,7 @@ changes:
31873190
coercion.
31883191
-->
31893192

3190-
Type: Documentation-only
3193+
Type: Runtime
31913194

31923195
Values other than `undefined`, `null`, integer numbers, and integer strings
31933196
(e.g., `'1'`) are deprecated as value for the `code` parameter in

lib/internal/bootstrap/node.js

+25
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,31 @@ process.domain = null;
102102
}
103103
process._exiting = false;
104104

105+
{
106+
const warnIntegerCoercionDeprecation = deprecate(
107+
() => {},
108+
'Implicit coercion to integer for exit code is deprecated.',
109+
'DEP0164'
110+
);
111+
112+
let exitCode;
113+
114+
ObjectDefineProperty(process, 'exitCode', {
115+
__proto__: null,
116+
get() {
117+
return exitCode;
118+
},
119+
set(code) {
120+
if (perThreadSetup.isDeprecatedExitCode(code)) {
121+
warnIntegerCoercionDeprecation();
122+
}
123+
exitCode = code;
124+
},
125+
enumerable: true,
126+
configurable: false,
127+
});
128+
}
129+
105130
// process.config is serialized config.gypi
106131
const nativeModule = internalBinding('builtins');
107132

lib/internal/process/per_thread.js

+35
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ const {
1313
ArrayPrototypeSplice,
1414
BigUint64Array,
1515
Float64Array,
16+
Number,
17+
NumberIsInteger,
1618
NumberMAX_SAFE_INTEGER,
19+
NumberMIN_SAFE_INTEGER,
1720
ObjectFreeze,
1821
ObjectDefineProperty,
1922
ReflectApply,
@@ -180,9 +183,23 @@ function wrapProcessMethods(binding) {
180183

181184
memoryUsage.rss = rss;
182185

186+
const { deprecate } = require('internal/util');
187+
const warnIntegerCoercionDeprecationSync = deprecate(
188+
() => {},
189+
'Implicit coercion to integer for exit code is deprecated.',
190+
'DEP0164',
191+
true
192+
);
193+
183194
function exit(code) {
184195
process.off('exit', handleProcessExit);
185196

197+
if (isDeprecatedExitCode(code)) {
198+
// Emit the deprecation warning synchronously since deprecation warning is
199+
// generally emitted in a next tick but we have no next tick timing here.
200+
warnIntegerCoercionDeprecationSync();
201+
}
202+
186203
if (code || code === 0)
187204
process.exitCode = code;
188205

@@ -407,6 +424,23 @@ function toggleTraceCategoryState(asyncHooksEnabled) {
407424
}
408425
}
409426

427+
function isDeprecatedExitCode(code) {
428+
if (code !== null && code !== undefined) {
429+
const value =
430+
typeof code === 'string' && code !== '' ? Number(code) : code;
431+
// Check if the value is an integer.
432+
if (
433+
typeof value !== 'number' ||
434+
!NumberIsInteger(value) ||
435+
value < NumberMIN_SAFE_INTEGER ||
436+
value > NumberMAX_SAFE_INTEGER
437+
) {
438+
return true;
439+
}
440+
}
441+
return false;
442+
}
443+
410444
module.exports = {
411445
toggleTraceCategoryState,
412446
assert,
@@ -415,4 +449,5 @@ module.exports = {
415449
hrtime,
416450
hrtimeBigInt,
417451
refreshHrtimeBuffer,
452+
isDeprecatedExitCode,
418453
};

lib/internal/process/warning.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,8 @@ function emitWarning(warning, type, code, ctor) {
166166
process.nextTick(doEmitWarning, warning);
167167
}
168168

169-
function emitWarningSync(warning) {
170-
process.emit('warning', createWarningObject(warning));
169+
function emitWarningSync(warning, type, code, ctor) {
170+
process.emit('warning', createWarningObject(warning, type, code, ctor));
171171
}
172172

173173
function createWarningObject(warning, type, code, ctor, detail) {

lib/internal/util.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ let validateString;
9696
// Mark that a method should not be used.
9797
// Returns a modified function which warns once by default.
9898
// If --no-deprecation is set, then it is a no-op.
99-
function deprecate(fn, msg, code) {
99+
function deprecate(fn, msg, code, useEmitSync) {
100100
if (process.noDeprecation === true) {
101101
return fn;
102102
}
@@ -114,7 +114,10 @@ function deprecate(fn, msg, code) {
114114
warned = true;
115115
if (code !== undefined) {
116116
if (!codesWarned.has(code)) {
117-
process.emitWarning(msg, 'DeprecationWarning', code, deprecated);
117+
const emitWarning = useEmitSync ?
118+
require('internal/process/warning').emitWarningSync :
119+
process.emitWarning;
120+
emitWarning(msg, 'DeprecationWarning', code, deprecated);
118121
codesWarned.add(code);
119122
}
120123
} else {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
'use strict';
2+
3+
require('../common');
4+
5+
const deprecated = [
6+
{
7+
code: '',
8+
expected: 0,
9+
},
10+
{
11+
code: '1 one',
12+
expected: 0,
13+
},
14+
{
15+
code: 'two',
16+
expected: 0,
17+
},
18+
{
19+
code: {},
20+
expected: 0,
21+
},
22+
{
23+
code: [],
24+
expected: 0,
25+
},
26+
{
27+
code: true,
28+
expected: 1,
29+
},
30+
{
31+
code: false,
32+
expected: 0,
33+
},
34+
{
35+
code: 2n,
36+
expected: 0,
37+
expected_useProcessExitCode: 1,
38+
},
39+
{
40+
code: 2.1,
41+
expected: 2,
42+
},
43+
{
44+
code: Infinity,
45+
expected: 0,
46+
},
47+
{
48+
code: NaN,
49+
expected: 0,
50+
},
51+
];
52+
const args = deprecated;
53+
54+
if (process.argv[2] === undefined) {
55+
const { spawnSync } = require('node:child_process');
56+
const { inspect, debuglog } = require('node:util');
57+
const { strictEqual } = require('node:assert');
58+
59+
const debug = debuglog('test');
60+
const node = process.execPath;
61+
const test = (index, useProcessExitCode) => {
62+
const { status: code, stderr } = spawnSync(node, [
63+
__filename,
64+
index,
65+
useProcessExitCode,
66+
]);
67+
debug(`actual: ${code}, ${inspect(args[index])} ${!!useProcessExitCode}`);
68+
debug(`${stderr}`);
69+
70+
const expected =
71+
useProcessExitCode && args[index].expected_useProcessExitCode ?
72+
args[index].expected_useProcessExitCode :
73+
args[index].expected;
74+
75+
strictEqual(code, expected, `actual: ${code}, ${inspect(args[index])}`);
76+
strictEqual(
77+
['[DEP0164]'].some((pattern) => stderr.includes(pattern)),
78+
true
79+
);
80+
};
81+
82+
for (const index of args.keys()) {
83+
// Check `process.exit([code])`
84+
test(index);
85+
// Check exit with `process.exitCode`
86+
test(index, true);
87+
}
88+
} else {
89+
const index = parseInt(process.argv[2]);
90+
const useProcessExitCode = process.argv[3] !== 'undefined';
91+
if (Number.isNaN(index)) {
92+
return process.exit(100);
93+
}
94+
95+
if (useProcessExitCode) {
96+
process.exitCode = args[index].code;
97+
} else {
98+
process.exit(args[index].code);
99+
}
100+
}

0 commit comments

Comments
 (0)