process: remove process.exit(), process.exitCode coercion to integer

This removes the deprecation, `DEP0164` and validates the exit code for
both `process.exit([code])` and `process.exitCode`.

Signed-off-by: Daeyeon Jeong <daeyeon.dev@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/43716
Refs: https://github.com/nodejs/node/pull/43738
Refs: https://github.com/nodejs/node/pull/44714
Refs: https://github.com/nodejs/node/pull/44711
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Darshan Sen <raisinten@gmail.com>
This commit is contained in:
Daeyeon Jeong 2022-10-20 03:40:22 +09:00 committed by GitHub
parent 19f3973828
commit 2d0d99733b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 176 additions and 148 deletions

View File

@ -3178,6 +3178,9 @@ thing instead.
<!-- YAML
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/43716
description: End-of-Life.
- version: v19.0.0
pr-url: https://github.com/nodejs/node/pull/44711
description: Runtime deprecation.
@ -3195,7 +3198,7 @@ changes:
coercion.
-->
Type: Runtime
Type: End-of-Life
Values other than `undefined`, `null`, integer numbers, and integer strings
(e.g., `'1'`) are deprecated as value for the `code` parameter in

View File

@ -1708,9 +1708,15 @@ that started the Node.js process. Symbolic links, if any, are resolved.
<!-- YAML
added: v0.1.13
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/43716
description: Only accepts a code of type number, or of type string if it
represents an integer.
-->
* `code` {integer} The exit code. **Default:** `0`.
* `code` {integer|string|null|undefined} The exit code. For string type, only
integer strings (e.g.,'1') are allowed. **Default:** `0`.
The `process.exit()` method instructs Node.js to terminate the process
synchronously with an exit status of `code`. If `code` is omitted, exit uses
@ -1810,9 +1816,15 @@ than the current process.
<!-- YAML
added: v0.11.8
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/43716
description: Only accepts a code of type number, or of type string if it
represents an integer.
-->
* {integer}
* {integer|string|null|undefined} The exit code. For string type, only
integer strings (e.g.,'1') are allowed. **Default:** `undefined`.
A number which will be the process exit code, when the process either
exits gracefully, or is exited via [`process.exit()`][] without specifying

View File

@ -60,6 +60,8 @@ const {
ArrayPrototypeFill,
FunctionPrototypeCall,
JSONParse,
Number,
NumberIsNaN,
ObjectDefineProperty,
ObjectGetPrototypeOf,
ObjectSetPrototypeOf,
@ -74,6 +76,9 @@ const {
deprecate,
exposeInterface,
} = require('internal/util');
const {
validateInteger,
} = require('internal/validators');
const {
exiting_aliased_Uint32Array,
getHiddenValue,
@ -103,12 +108,6 @@ process.domain = null;
process._exiting = false;
{
const warnIntegerCoercionDeprecation = deprecate(
() => {},
'Implicit coercion to integer for exit code is deprecated.',
'DEP0164'
);
let exitCode;
ObjectDefineProperty(process, 'exitCode', {
@ -117,8 +116,13 @@ process._exiting = false;
return exitCode;
},
set(code) {
if (perThreadSetup.isDeprecatedExitCode(code)) {
warnIntegerCoercionDeprecation();
if (code !== null && code !== undefined) {
let value = code;
if (typeof code === 'string' && code !== '' &&
NumberIsNaN((value = Number(code)))) {
value = code;
}
validateInteger(value, 'code');
}
exitCode = code;
},

View File

@ -13,10 +13,7 @@ const {
ArrayPrototypeSplice,
BigUint64Array,
Float64Array,
Number,
NumberIsInteger,
NumberMAX_SAFE_INTEGER,
NumberMIN_SAFE_INTEGER,
ObjectFreeze,
ObjectDefineProperty,
ReflectApply,
@ -183,25 +180,12 @@ function wrapProcessMethods(binding) {
memoryUsage.rss = rss;
const { deprecate } = require('internal/util');
const warnIntegerCoercionDeprecationSync = deprecate(
() => {},
'Implicit coercion to integer for exit code is deprecated.',
'DEP0164',
true
);
function exit(code) {
process.off('exit', handleProcessExit);
if (isDeprecatedExitCode(code)) {
// Emit the deprecation warning synchronously since deprecation warning is
// generally emitted in a next tick but we have no next tick timing here.
warnIntegerCoercionDeprecationSync();
}
if (code || code === 0)
if (arguments.length !== 0) {
process.exitCode = code;
}
if (!process._exiting) {
process._exiting = true;
@ -424,23 +408,6 @@ function toggleTraceCategoryState(asyncHooksEnabled) {
}
}
function isDeprecatedExitCode(code) {
if (code !== null && code !== undefined) {
const value =
typeof code === 'string' && code !== '' ? Number(code) : code;
// Check if the value is an integer.
if (
typeof value !== 'number' ||
!NumberIsInteger(value) ||
value < NumberMIN_SAFE_INTEGER ||
value > NumberMAX_SAFE_INTEGER
) {
return true;
}
}
return false;
}
module.exports = {
toggleTraceCategoryState,
assert,
@ -449,5 +416,4 @@ module.exports = {
hrtime,
hrtimeBigInt,
refreshHrtimeBuffer,
isDeprecatedExitCode,
};

View File

@ -1,100 +0,0 @@
'use strict';
require('../common');
const deprecated = [
{
code: '',
expected: 0,
},
{
code: '1 one',
expected: 0,
},
{
code: 'two',
expected: 0,
},
{
code: {},
expected: 0,
},
{
code: [],
expected: 0,
},
{
code: true,
expected: 1,
},
{
code: false,
expected: 0,
},
{
code: 2n,
expected: 0,
expected_useProcessExitCode: 1,
},
{
code: 2.1,
expected: 2,
},
{
code: Infinity,
expected: 0,
},
{
code: NaN,
expected: 0,
},
];
const args = deprecated;
if (process.argv[2] === undefined) {
const { spawnSync } = require('node:child_process');
const { inspect, debuglog } = require('node:util');
const { strictEqual } = require('node:assert');
const debug = debuglog('test');
const node = process.execPath;
const test = (index, useProcessExitCode) => {
const { status: code, stderr } = spawnSync(node, [
__filename,
index,
useProcessExitCode,
]);
debug(`actual: ${code}, ${inspect(args[index])} ${!!useProcessExitCode}`);
debug(`${stderr}`);
const expected =
useProcessExitCode && args[index].expected_useProcessExitCode ?
args[index].expected_useProcessExitCode :
args[index].expected;
strictEqual(code, expected, `actual: ${code}, ${inspect(args[index])}`);
strictEqual(
['[DEP0164]'].some((pattern) => stderr.includes(pattern)),
true
);
};
for (const index of args.keys()) {
// Check `process.exit([code])`
test(index);
// Check exit with `process.exitCode`
test(index, true);
}
} else {
const index = parseInt(process.argv[2]);
const useProcessExitCode = process.argv[3] !== 'undefined';
if (Number.isNaN(index)) {
return process.exit(100);
}
if (useProcessExitCode) {
process.exitCode = args[index].code;
} else {
process.exit(args[index].code);
}
}

View File

@ -0,0 +1,143 @@
'use strict';
require('../common');
const invalids = [
{
code: '',
expected: 1,
pattern: "Received type string \\(''\\)$",
},
{
code: '1 one',
expected: 1,
pattern: "Received type string \\('1 one'\\)$",
},
{
code: 'two',
expected: 1,
pattern: "Received type string \\('two'\\)$",
},
{
code: {},
expected: 1,
pattern: 'Received an instance of Object$',
},
{
code: [],
expected: 1,
pattern: 'Received an instance of Array$',
},
{
code: true,
expected: 1,
pattern: 'Received type boolean \\(true\\)$',
},
{
code: false,
expected: 1,
pattern: 'Received type boolean \\(false\\)$',
},
{
code: 2n,
expected: 1,
pattern: 'Received type bigint \\(2n\\)$',
},
{
code: 2.1,
expected: 1,
pattern: 'Received 2.1$',
},
{
code: Infinity,
expected: 1,
pattern: 'Received Infinity$',
},
{
code: NaN,
expected: 1,
pattern: 'Received NaN$',
},
];
const valids = [
{
code: 1,
expected: 1,
},
{
code: '2',
expected: 2,
},
{
code: undefined,
expected: 0,
},
{
code: null,
expected: 0,
},
{
code: 0,
expected: 0,
},
{
code: '0',
expected: 0,
},
];
const args = [...invalids, ...valids];
if (process.argv[2] === undefined) {
const { spawnSync } = require('node:child_process');
const { inspect, debuglog } = require('node:util');
const { throws, strictEqual } = require('node:assert');
const debug = debuglog('test');
const node = process.execPath;
const test = (index, useProcessExitCode) => {
const { status: code } = spawnSync(node, [
__filename,
index,
useProcessExitCode,
]);
debug(`actual: ${code}, ${inspect(args[index])} ${!!useProcessExitCode}`);
strictEqual(
code,
args[index].expected,
`actual: ${code}, ${inspect(args[index])}`
);
};
// Check process.exitCode
for (const arg of invalids) {
debug(`invaild code: ${inspect(arg.code)}`);
throws(() => (process.exitCode = arg.code), new RegExp(arg.pattern));
}
for (const arg of valids) {
debug(`vaild code: ${inspect(arg.code)}`);
process.exitCode = arg.code;
}
throws(() => {
delete process.exitCode;
}, /Cannot delete property 'exitCode' of #<process>/);
process.exitCode = 0;
// Check process.exit([code])
for (const index of args.keys()) {
test(index);
test(index, true);
}
} else {
const index = parseInt(process.argv[2]);
const useProcessExitCode = process.argv[3] !== 'undefined';
if (Number.isNaN(index)) {
return process.exit(100);
}
if (useProcessExitCode) {
process.exitCode = args[index].code;
} else {
process.exit(args[index].code);
}
}

View File

@ -33,7 +33,7 @@ if (isMainThread) {
assert.strictEqual(headers[':status'], 200);
}));
req.on('data', common.mustCall(process.exit));
req.on('data', common.mustCall(() => process.exit()));
req.on('end', common.mustNotCall());
req.end();
}