repl: don't use deprecated domain module#55204
repl: don't use deprecated domain module#55204avivkeller wants to merge 1 commit intonodejs:mainfrom
domain module#55204Conversation
This comment was marked as resolved.
This comment was marked as resolved.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #55204 +/- ##
========================================
Coverage 88.41% 88.42%
========================================
Files 652 654 +2
Lines 186918 187688 +770
Branches 36072 36126 +54
========================================
+ Hits 165270 165956 +686
- Misses 14891 14963 +72
- Partials 6757 6769 +12
🚀 New features to boost your workflow:
|
42e6441 to
fb9057d
Compare
47e132b to
9be6f17
Compare
9be6f17 to
5f6d5ec
Compare
mcollina
left a comment
There was a problem hiding this comment.
I have a rough feeling that you'd need to add
a FinalizationRegistry to clear up any instances
Why? Is unsetting the callback on |
5f6d5ec to
dcf5e94
Compare
dcf5e94 to
b0d6991
Compare
This comment has been minimized.
This comment has been minimized.
I doubt we have any package on CITGM that use repl, do we? |
|
Got it! I've removed the label. IIRC semver-major PRs need two TSC approvals to land, would you mind reviewing? |
|
@nodejs/tsc add it to the agenda for visibility. |
|
|
||
| * Uncaught exceptions only emit the [`'uncaughtException'`][] event in the | ||
| standalone REPL. Adding a listener for this event in a REPL within | ||
| another Node.js program results in [`ERR_INVALID_REPL_INPUT`][]. |
There was a problem hiding this comment.
I'd replace this section with one that describes the new behavior and amend the changes: YAML entry instead of dropping it (since the new behavior is definitely one that doesn't matter to some users but will have a significant negative impact on users who rely on it)
There was a problem hiding this comment.
I don't think a specific section is needed, IMO there's nothing to document. it adds a listener to the process object, what negative impact does that have?
There was a problem hiding this comment.
@redyetidev Well, before this change, uncaughtException listeners wouldn't have seen uncaught exceptions coming from the REPL, and now they do, right? It's not a massive change or anything, but I'd still document it somewhere. If you feel that no separate section is necessary, I'd still keep the changes: block to document the change in behavior over time
There was a problem hiding this comment.
That's incorrect. Before this change, uncaughtException listeners still saw REPL exceptions
There was a problem hiding this comment.
I am confused about this change. As far as I see it in the code, only the stand alone repl works with the uncaughtException listeners. In case more than a single REPL instance is running, it is not possible to identify the correct listeners, so none receive the errors.
There was a problem hiding this comment.
Needs docs; I'd also add a test for the specific behavior that we now have when two separate REPL instances receive the same uncaught exception.
There's probably even room for a way to restore the current behavior (which is imo the clearly correct one, even if it makes use of code that's deprecated for good reasons) – we'd a) need to make the REPL take an option that tells it whether to install the global I probably misread the code and the current logic is trying to re-establish this behavior, all good.process.on('uncaughtException') listener and b) allow users to set that flag to off, and start a REPL within a domain on their own + move errors from the domain to the REPL instance. That's not too complex, I think.
That won't happen. It's specifically set up so that an uncaught exception triggered from REPL (A) won't affect REPL (B). A test to ensure this is https://github.com/nodejs/node/pull/55204/files#diff-ca6f6e3faa6e2dc3fba74eabfc212f36fa2f84b7a17e3f73662d24b678d18f73 (although it doesn't check two REPLs, rather it verifies that an uncaught exception triggered from outside the REPL does not affect the REPL, which [testing-wise] is a similar test) |
BridgeAR
left a comment
There was a problem hiding this comment.
This is definitely nice work, I still think there are a few open parts though.
| @@ -612,13 +626,8 @@ function REPLServer(prompt, | |||
| } | |||
| } catch (e) { | |||
| err = e; | |||
There was a problem hiding this comment.
Nit: the assignment is AFAIK not needed anymore
| }, | ||
| }); | ||
|
|
||
| let kHasSetUncaughtListener = false; |
There was a problem hiding this comment.
This is not a constant, so I would name the variable different.
| let kHasSetUncaughtListener = false; | |
| let hasUncaughtExceptionListener = false; |
| } | ||
| }; | ||
|
|
||
| self._onEvalError = function _onEvalError(e) { |
There was a problem hiding this comment.
It would be great not to expose this property.
I would just pass the instance to the method so that this method is a purely internal one.
For testing purposes, we would only have to verify that nothing else is written to REPLinstance.output.write().
| self._domain.on('error', function debugDomainError(e) { | ||
| debug('domain error'); | ||
| try { | ||
| FunctionPrototypeApply(eval_, this, args); |
There was a problem hiding this comment.
eval_ could be an async function provided by the user. Would it be a good idea to await that method? I am not certain if that would improve things or if it makes it more difficult
| const kLoadingSymbol = Symbol('loading'); | ||
| const kListeningREPLs = new SafeSet(); | ||
| const kAsyncREPLMap = new SafeMap(); | ||
| let kActiveREPL; |
There was a problem hiding this comment.
| let kActiveREPL; | |
| let activeREPL; |
| if (kActiveREPL) { | ||
| kActiveREPL._onEvalError(er); |
There was a problem hiding this comment.
Are we definitely safe, that the active repl is definitely the correct one?
Let's say one repl starts an operation at time 0 and the operation needs 4 time intervals. A second repl instance start an operation at time 1 and that operation needs 2 intervals. The first instance fails after 2 intervals. Which repl instance is the error now reported upon? Do we maybe have a test for this?
|
|
||
| * Uncaught exceptions only emit the [`'uncaughtException'`][] event in the | ||
| standalone REPL. Adding a listener for this event in a REPL within | ||
| another Node.js program results in [`ERR_INVALID_REPL_INPUT`][]. |
There was a problem hiding this comment.
I am confused about this change. As far as I see it in the code, only the stand alone repl works with the uncaughtException listeners. In case more than a single REPL instance is running, it is not possible to identify the correct listeners, so none receive the errors.
This PR refactors the REPL module by eliminating the deprecated
node:domainmodule. It replaces its functionality with a modern approach using atry/catchblock andprocesslistener.Why?
The
domainmodule has been deprecated for a while, and its use is discouraged. Despite this, Node.js still relies on it in a key component: the REPL. To lead by example and promote best practices, this PR updates the REPL module to use more up-to-date error-handling mechanisms, completely removing reliance on the deprecateddomainmodule.Additionally, this update simplifies the REPL's code and results in a slight performance improvement—though the gains are give-or-take a few ticks and may not be noticeable.
Breaking Changes
This PR is marked as semver-majorPRs that contain breaking changes and should be released in the next major version.
because it introduces functional changes to the REPL:
options.domain: This undocumented feature is removed (without a formal deprecation cycle, given thedomainmodule itself is deprecated, and the feature was never documented nor tested).ERR_INVALID_REPL_INPUT: This error is no longer needed, as after this change, there are no restricted inputs.This PR is also marked needs-citgmPRs that need a CITGM CI run.
in case any of the above cause breakages.
CC @nodejs/replPRs that contain breaking changes and should be released in the next major version.
CC @nodejs/tsc due to semver-major