Skip to content

Commit 80a0041

Browse files
src: fix process exit listeners not receiving unsettled tla codes
fix listeners registered via `process.on('exit', ...` not receiving error code 13 when an unsettled top-level-await is encountered in the code
1 parent 2bd5694 commit 80a0041

File tree

5 files changed

+48
-25
lines changed

5 files changed

+48
-25
lines changed

src/api/embed_helpers.cc

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -73,20 +73,7 @@ Maybe<ExitCode> SpinEventLoopInternal(Environment* env) {
7373

7474
env->PrintInfoForSnapshotIfDebug();
7575
env->ForEachRealm([](Realm* realm) { realm->VerifyNoStrongBaseObjects(); });
76-
Maybe<ExitCode> exit_code = EmitProcessExitInternal(env);
77-
if (exit_code.FromMaybe(ExitCode::kGenericUserError) !=
78-
ExitCode::kNoFailure) {
79-
return exit_code;
80-
}
81-
82-
auto unsettled_tla = env->CheckUnsettledTopLevelAwait();
83-
if (unsettled_tla.IsNothing()) {
84-
return Nothing<ExitCode>();
85-
}
86-
if (!unsettled_tla.FromJust()) {
87-
return Just(ExitCode::kUnsettledTopLevelAwait);
88-
}
89-
return Just(ExitCode::kNoFailure);
76+
return EmitProcessExitInternal(env);
9077
}
9178

9279
struct CommonEnvironmentSetup::Impl {

src/api/hooks.cc

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,25 @@ Maybe<ExitCode> EmitProcessExitInternal(Environment* env) {
7070
return Nothing<ExitCode>();
7171
}
7272

73-
Local<Integer> exit_code = Integer::New(
74-
isolate, static_cast<int32_t>(env->exit_code(ExitCode::kNoFailure)));
73+
ExitCode exit_code = env->exit_code(ExitCode::kNoFailure);
74+
75+
// the exit code wasn't already set, so let's check for unsettled tlas
76+
if (exit_code == ExitCode::kNoFailure) {
77+
auto unsettled_tla = env->CheckUnsettledTopLevelAwait();
78+
if (!unsettled_tla.FromJust()) {
79+
exit_code = ExitCode::kUnsettledTopLevelAwait;
80+
}
81+
}
7582

76-
if (ProcessEmit(env, "exit", exit_code).IsEmpty()) {
83+
Local<Integer> exit_code_int =
84+
Integer::New(isolate, static_cast<int32_t>(exit_code));
85+
86+
if (ProcessEmit(env, "exit", exit_code_int).IsEmpty()) {
7787
return Nothing<ExitCode>();
7888
}
89+
7990
// Reload exit code, it may be changed by `emit('exit')`
80-
return Just(env->exit_code(ExitCode::kNoFailure));
91+
return Just(env->exit_code(exit_code));
8192
}
8293

8394
Maybe<int> EmitProcessExit(Environment* env) {

test/es-module/test-esm-tla-unfinished.mjs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,24 +72,31 @@ describe('ESM: unsettled and rejected promises', { concurrency: !process.env.TES
7272
});
7373

7474
it('should exit for an unsettled TLA promise with warning', async () => {
75-
const { code, stderr, stdout } = await spawnPromisified(execPath, [
75+
const { code, stderr } = await spawnPromisified(execPath, [
7676
fixtures.path('es-modules/tla/unresolved.mjs'),
7777
]);
7878

79-
assert.match(stderr, /Warning: Detected unsettled top-level await at.+unresolved\.mjs:1/);
79+
assert.match(stderr, /Warning: Detected unsettled top-level await at.+unresolved\.mjs:5/);
8080
assert.match(stderr, /await new Promise/);
81-
assert.strictEqual(stdout, '');
81+
assert.strictEqual(code, 13);
82+
});
83+
84+
it('the process exit event should provide the correct code for an unsettled TLA promise', async () => {
85+
const { code, stdout } = await spawnPromisified(execPath, [
86+
fixtures.path('es-modules/tla/unresolved.mjs'),
87+
]);
88+
89+
assert.strictEqual(stdout, 'the exit listener received code: 13\n');
8290
assert.strictEqual(code, 13);
8391
});
8492

8593
it('should exit for an unsettled TLA promise without warning', async () => {
86-
const { code, stderr, stdout } = await spawnPromisified(execPath, [
94+
const { code, stderr } = await spawnPromisified(execPath, [
8795
'--no-warnings',
8896
fixtures.path('es-modules/tla/unresolved.mjs'),
8997
]);
9098

9199
assert.strictEqual(stderr, '');
92-
assert.strictEqual(stdout, '');
93100
assert.strictEqual(code, 13);
94101
});
95102

@@ -105,13 +112,22 @@ describe('ESM: unsettled and rejected promises', { concurrency: !process.env.TES
105112
});
106113

107114
it('should exit for an unsettled TLA promise and respect explicit exit code via stdin', async () => {
108-
const { code, stderr, stdout } = await spawnPromisified(execPath, [
115+
const { code, stderr } = await spawnPromisified(execPath, [
109116
'--no-warnings',
110117
fixtures.path('es-modules/tla/unresolved-withexitcode.mjs'),
111118
]);
112119

113120
assert.strictEqual(stderr, '');
114-
assert.strictEqual(stdout, '');
121+
assert.strictEqual(code, 42);
122+
});
123+
124+
it('should exit for an unsettled TLA promise and respect explicit exit code in process exit event', async () => {
125+
const { code, stdout } = await spawnPromisified(execPath, [
126+
'--no-warnings',
127+
fixtures.path('es-modules/tla/unresolved-withexitcode.mjs'),
128+
]);
129+
130+
assert.strictEqual(stdout, 'the exit listener received code: 42\n');
115131
assert.strictEqual(code, 42);
116132
});
117133

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
1+
process.on('exit', (exitCode) => {
2+
console.log(`the exit listener received code: ${exitCode}`);
3+
});
4+
15
process.exitCode = 42;
6+
27
await new Promise(() => {});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1+
process.on('exit', (exitCode) => {
2+
console.log(`the exit listener received code: ${exitCode}`);
3+
})
4+
15
await new Promise(() => {});

0 commit comments

Comments
 (0)