| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 0ad450f commit 5e51c62
11 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1893,8 +1893,28 @@ A number which will be the process exit code, when the process either | |||
| 1893 | 1893 | exits gracefully, or is exited via [`process.exit()`][] without specifying | |
| 1894 | 1894 | a code. | |
| 1895 | 1895 | ||
| 1896 | - Specifying a code to [`process.exit(code)`][`process.exit()`] will override any | ||
| 1897 | - previous setting of `process.exitCode`. | ||
| 1896 | + The value of `process.exitCode` can be updated by either assigning a value to | ||
| 1897 | + `process.exitCode` or by passing an argument to [`process.exit()`][]: | ||
| 1898 | + | ||
| 1899 | + ```console | ||
| 1900 | + $ node -e 'process.exitCode = 9'; echo $? | ||
| 1901 | + 9 | ||
| 1902 | + $ node -e 'process.exit(42)'; echo $? | ||
| 1903 | + 42 | ||
| 1904 | + $ node -e 'process.exitCode = 9; process.exit(42)'; echo $? | ||
| 1905 | + 42 | ||
| 1906 | + ``` | ||
| 1907 | + | ||
| 1908 | + The value can also be set implicitly by Node.js when unrecoverable errors occur (e.g. | ||
| 1909 | + such as the encountering of an unsettled top-level await). However explicit | ||
| 1910 | + manipulations of the exit code always take precedence over implicit ones: | ||
| 1911 | + | ||
| 1912 | + ```console | ||
| 1913 | + $ node --input-type=module -e 'await new Promise(() => {})'; echo $? | ||
| 1914 | + 13 | ||
| 1915 | + $ node --input-type=module -e 'process.exitCode = 9; await new Promise(() => {})'; echo $? | ||
| 1916 | + 9 | ||
| 1917 | + ``` | ||
| 1898 | 1918 | ||
| 1899 | 1919 | ## `process.features.cached_builtins` | |
| 1900 | 1920 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -104,11 +104,10 @@ process.domain = null; | |||
| 104 | 104 | configurable: true, | |
| 105 | 105 | }); | |
| 106 | 106 | ||
| 107 | - let exitCode; | ||
| 108 | 107 | ObjectDefineProperty(process, 'exitCode', { | |
| 109 | 108 | __proto__: null, | |
| 110 | 109 | get() { | |
| 111 | - return exitCode; | ||
| 110 | + return fields[kHasExitCode] ? fields[kExitCode] : undefined; | ||
| 112 | 111 | }, | |
| 113 | 112 | set(code) { | |
| 114 | 113 | if (code !== null && code !== undefined) { | |
@@ -123,7 +122,6 @@ process.domain = null; | |||
| 123 | 122 | } else { | |
| 124 | 123 | fields[kHasExitCode] = 0; | |
| 125 | 124 | } | |
| 126 | - exitCode = code; | ||
| 127 | 125 | }, | |
| 128 | 126 | enumerable: true, | |
| 129 | 127 | configurable: false, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -73,20 +73,7 @@ Maybe<ExitCode> SpinEventLoopInternal(Environment* env) { | |||
| 73 | 73 | ||
| 74 | 74 | env->PrintInfoForSnapshotIfDebug(); | |
| 75 | 75 | 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); | ||
| 90 | 77 | } | |
| 91 | 78 | ||
| 92 | 79 | struct CommonEnvironmentSetup::Impl { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -70,14 +70,26 @@ Maybe<ExitCode> EmitProcessExitInternal(Environment* env) { | |||
| 70 | 70 | return Nothing<ExitCode>(); | |
| 71 | 71 | } | |
| 72 | 72 | ||
| 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 | + env->set_exit_code(exit_code); | ||
| 81 | + } | ||
| 82 | + } | ||
| 75 | 83 | ||
| 76 | - if (ProcessEmit(env, "exit", exit_code).IsEmpty()) { | ||
| 84 | + Local<Integer> exit_code_int = | ||
| 85 | + Integer::New(isolate, static_cast<int32_t>(exit_code)); | ||
| 86 | + | ||
| 87 | + if (ProcessEmit(env, "exit", exit_code_int).IsEmpty()) { | ||
| 77 | 88 | return Nothing<ExitCode>(); | |
| 78 | 89 | } | |
| 90 | + | ||
| 79 | 91 | // Reload exit code, it may be changed by `emit('exit')` | |
| 80 | - return Just(env->exit_code(ExitCode::kNoFailure)); | ||
| 92 | + return Just(env->exit_code(exit_code)); | ||
| 81 | 93 | } | |
| 82 | 94 | ||
| 83 | 95 | Maybe<int> EmitProcessExit(Environment* env) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -341,6 +341,11 @@ inline ExitCode Environment::exit_code(const ExitCode default_code) const { | |||
| 341 | 341 | : static_cast<ExitCode>(exit_info_[kExitCode]); | |
| 342 | 342 | } | |
| 343 | 343 | ||
| 344 | + inline void Environment::set_exit_code(const ExitCode code) { | ||
| 345 | + exit_info_[kExitCode] = static_cast<int>(code); | ||
| 346 | + exit_info_[kHasExitCode] = 1; | ||
| 347 | + } | ||
| 348 | + | ||
| 344 | 349 | inline AliasedInt32Array& Environment::exit_info() { | |
| 345 | 350 | return exit_info_; | |
| 346 | 351 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -739,6 +739,8 @@ class Environment final : public MemoryRetainer { | |||
| 739 | 739 | bool exiting() const; | |
| 740 | 740 | inline ExitCode exit_code(const ExitCode default_code) const; | |
| 741 | 741 | ||
| 742 | + inline void set_exit_code(const ExitCode code); | ||
| 743 | + | ||
| 742 | 744 | // This stores whether the --abort-on-uncaught-exception flag was passed | |
| 743 | 745 | // to Node. | |
| 744 | 746 | inline bool abort_on_uncaught_exception() const; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -76,9 +76,9 @@ describe('ESM: unsettled and rejected promises', { concurrency: !process.env.TES | |||
| 76 | 76 | fixtures.path('es-modules/tla/unresolved.mjs'), | |
| 77 | 77 | ]); | |
| 78 | 78 | ||
| 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\b/); | ||
| 80 | 80 | assert.match(stderr, /await new Promise/); | |
| 81 | - assert.strictEqual(stdout, ''); | ||
| 81 | + assert.strictEqual(stdout, 'the exit listener received code: 13\n'); | ||
| 82 | 82 | assert.strictEqual(code, 13); | |
| 83 | 83 | }); | |
| 84 | 84 | ||
@@ -88,9 +88,11 @@ describe('ESM: unsettled and rejected promises', { concurrency: !process.env.TES | |||
| 88 | 88 | fixtures.path('es-modules/tla/unresolved.mjs'), | |
| 89 | 89 | ]); | |
| 90 | 90 | ||
| 91 | - assert.strictEqual(stderr, ''); | ||
| 92 | - assert.strictEqual(stdout, ''); | ||
| 93 | - assert.strictEqual(code, 13); | ||
| 91 | + assert.deepStrictEqual({ code, stdout, stderr }, { | ||
| 92 | + code: 13, | ||
| 93 | + stdout: 'the exit listener received code: 13\n', | ||
| 94 | + stderr: '', | ||
| 95 | + }); | ||
| 94 | 96 | }); | |
| 95 | 97 | ||
| 96 | 98 | it('should throw for a rejected TLA promise via stdin', async () => { | |
@@ -104,15 +106,17 @@ describe('ESM: unsettled and rejected promises', { concurrency: !process.env.TES | |||
| 104 | 106 | assert.strictEqual(code, 1); | |
| 105 | 107 | }); | |
| 106 | 108 | ||
| 107 | - it('should exit for an unsettled TLA promise and respect explicit exit code via stdin', async () => { | ||
| 109 | + it('should exit for an unsettled TLA promise and respect explicit exit code', async () => { | ||
| 108 | 110 | const { code, stderr, stdout } = await spawnPromisified(execPath, [ | |
| 109 | 111 | '--no-warnings', | |
| 110 | 112 | fixtures.path('es-modules/tla/unresolved-withexitcode.mjs'), | |
| 111 | 113 | ]); | |
| 112 | 114 | ||
| 113 | - assert.strictEqual(stderr, ''); | ||
| 114 | - assert.strictEqual(stdout, ''); | ||
| 115 | - assert.strictEqual(code, 42); | ||
| 115 | + assert.deepStrictEqual({ code, stdout, stderr }, { | ||
| 116 | + code: 42, | ||
| 117 | + stdout: 'the exit listener received code: 42\n', | ||
| 118 | + stderr: '', | ||
| 119 | + }); | ||
| 116 | 120 | }); | |
| 117 | 121 | ||
| 118 | 122 | it('should throw for a rejected TLA promise and ignore explicit exit code via stdin', async () => { | |
@@ -158,4 +162,33 @@ describe('ESM: unsettled and rejected promises', { concurrency: !process.env.TES | |||
| 158 | 162 | assert.strictEqual(stdout, ''); | |
| 159 | 163 | assert.strictEqual(code, 13); | |
| 160 | 164 | }); | |
| 165 | + | ||
| 166 | + describe('with exit listener', () => { | ||
| 167 | + it('the process exit event should provide the correct code', async () => { | ||
| 168 | + const { code, stderr, stdout } = await spawnPromisified(execPath, [ | ||
| 169 | + fixtures.path('es-modules/tla/unresolved-with-listener.mjs'), | ||
| 170 | + ]); | ||
| 171 | + | ||
| 172 | + assert.match(stderr, /Warning: Detected unsettled top-level await at/); | ||
| 173 | + assert.strictEqual(stdout, | ||
| 174 | + 'the exit listener received code: 13\n' + | ||
| 175 | + 'process.exitCode inside the exist listener: 13\n' | ||
| 176 | + ); | ||
| 177 | + assert.strictEqual(code, 13); | ||
| 178 | + }); | ||
| 179 | + | ||
| 180 | + it('should exit for an unsettled TLA promise and respect explicit exit code in process exit event', async () => { | ||
| 181 | + const { code, stderr, stdout } = await spawnPromisified(execPath, [ | ||
| 182 | + '--no-warnings', | ||
| 183 | + fixtures.path('es-modules/tla/unresolved-withexitcode-and-listener.mjs'), | ||
| 184 | + ]); | ||
| 185 | + | ||
| 186 | + assert.deepStrictEqual({ code, stdout, stderr }, { | ||
| 187 | + code: 42, | ||
| 188 | + stdout: 'the exit listener received code: 42\n' + | ||
| 189 | + 'process.exitCode inside the exist listener: 42\n', | ||
| 190 | + stderr: '', | ||
| 191 | + }); | ||
| 192 | + }); | ||
| 193 | + }); | ||
| 161 | 194 | }); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,6 @@ | |||
| 1 | + process.on('exit', (exitCode) => { | ||
| 2 | + console.log(`the exit listener received code: ${exitCode}`); | ||
| 3 | + console.log(`process.exitCode inside the exist listener: ${process.exitCode}`); | ||
| 4 | + }) | ||
| 5 | + | ||
| 6 | + await new Promise(() => {}); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,8 @@ | |||
| 1 | + process.on('exit', (exitCode) => { | ||
| 2 | + console.log(`the exit listener received code: ${exitCode}`); | ||
| 3 | + console.log(`process.exitCode inside the exist listener: ${process.exitCode}`); | ||
| 4 | + }); | ||
| 5 | + | ||
| 6 | + process.exitCode = 42; | ||
| 7 | + | ||
| 8 | + await new Promise(() => {}); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,2 +1,7 @@ | |||
| 1 | + process.on('exit', (exitCode) => { | ||
| 2 | + console.log(`the exit listener received code: ${exitCode}`); | ||
| 3 | + }); | ||
| 4 | + | ||
| 1 | 5 | process.exitCode = 42; | |
| 6 | + | ||
| 2 | 7 | await new Promise(() => {}); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments