@@ -60,6 +60,7 @@ const {
6060 ArrayPrototypeUnshift,
6161 Boolean,
6262 Error : MainContextError ,
63+ FunctionPrototypeApply,
6364 FunctionPrototypeBind,
6465 JSONStringify,
6566 MathMaxApply,
@@ -76,9 +77,9 @@ const {
7677 ReflectApply,
7778 RegExp,
7879 RegExpPrototypeExec,
80+ SafeMap,
7981 SafePromiseRace,
8082 SafeSet,
81- SafeWeakSet,
8283 StringPrototypeCharAt,
8384 StringPrototypeCodePointAt,
8485 StringPrototypeEndsWith,
@@ -138,7 +139,6 @@ ArrayPrototypeForEach(
138139 BuiltinModule . getSchemeOnlyModuleNames ( ) ,
139140 ( lib ) => ArrayPrototypePush ( nodeSchemeBuiltinLibs , `node:${ lib } ` ) ,
140141) ;
141- const domain = require ( 'domain' ) ;
142142let debug = require ( 'internal/util/debuglog' ) . debuglog ( 'repl' , ( fn ) => {
143143 debug = fn ;
144144} ) ;
@@ -147,7 +147,6 @@ const {
147147 codes : {
148148 ERR_CANNOT_WATCH_SIGINT ,
149149 ERR_INVALID_REPL_EVAL_CONFIG ,
150- ERR_INVALID_REPL_INPUT ,
151150 ERR_MISSING_ARGS ,
152151 ERR_SCRIPT_EXECUTION_INTERRUPTED ,
153152 } ,
@@ -191,6 +190,7 @@ const {
191190const {
192191 makeContextifyScript,
193192} = require ( 'internal/vm' ) ;
193+ const { createHook } = require ( 'async_hooks' ) ;
194194let nextREPLResourceNumber = 1 ;
195195// This prevents v8 code cache from getting confused and using a different
196196// cache from a resource of the same name
@@ -205,13 +205,43 @@ const globalBuiltins =
205205 new SafeSet ( vm . runInNewContext ( 'Object.getOwnPropertyNames(globalThis)' ) ) ;
206206
207207const parentModule = module ;
208- const domainSet = new SafeWeakSet ( ) ;
209208
210209const kBufferedCommandSymbol = Symbol ( 'bufferedCommand' ) ;
211210const kContextId = Symbol ( 'contextId' ) ;
212211const kLoadingSymbol = Symbol ( 'loading' ) ;
212+ const kListeningREPLs = new SafeSet ( ) ;
213+ const kAsyncREPLMap = new SafeMap ( ) ;
214+ let kActiveREPL ;
215+ const kAsyncHook = createHook ( {
216+ init ( asyncId ) {
217+ if ( kActiveREPL ) {
218+ kAsyncREPLMap . set ( asyncId , kActiveREPL ) ;
219+ }
220+ } ,
221+
222+ before ( asyncId ) {
223+ kActiveREPL = kAsyncREPLMap . get ( asyncId ) || kActiveREPL ;
224+ } ,
225+
226+ destroy ( asyncId ) {
227+ kAsyncREPLMap . delete ( asyncId ) ;
228+ } ,
229+ } ) ;
230+
231+ let kHasSetUncaughtListener = false ;
213232
214- let addedNewListener = false ;
233+ function handleUncaughtException ( er ) {
234+ kActiveREPL ?. _onEvalError ( er ) ;
235+ }
236+
237+ function removeListeningREPL ( repl ) {
238+ kListeningREPLs . delete ( repl ) ;
239+ if ( kListeningREPLs . size === 0 ) {
240+ kAsyncHook . disable ( ) ;
241+ kHasSetUncaughtListener = false ;
242+ process . off ( 'uncaughtException' , handleUncaughtException ) ;
243+ }
244+ }
215245
216246try {
217247 // Hack for require.resolve("./relative") to work properly.
@@ -346,7 +376,6 @@ function REPLServer(prompt,
346376
347377 this . allowBlockingCompletions = ! ! options . allowBlockingCompletions ;
348378 this . useColors = ! ! options . useColors ;
349- this . _domain = options . domain || domain . create ( ) ;
350379 this . useGlobal = ! ! useGlobal ;
351380 this . ignoreUndefined = ! ! ignoreUndefined ;
352381 this . replMode = replMode || module . exports . REPL_MODE_SLOPPY ;
@@ -369,28 +398,8 @@ function REPLServer(prompt,
369398 // It is possible to introspect the running REPL accessing this variable
370399 // from inside the REPL. This is useful for anyone working on the REPL.
371400 module . exports . repl = this ;
372- } else if ( ! addedNewListener ) {
373- // Add this listener only once and use a WeakSet that contains the REPLs
374- // domains. Otherwise we'd have to add a single listener to each REPL
375- // instance and that could trigger the `MaxListenersExceededWarning`.
376- process . prependListener ( 'newListener' , ( event , listener ) => {
377- if ( event === 'uncaughtException' &&
378- process . domain &&
379- listener . name !== 'domainUncaughtExceptionClear' &&
380- domainSet . has ( process . domain ) ) {
381- // Throw an error so that the event will not be added and the current
382- // domain takes over. That way the user is notified about the error
383- // and the current code evaluation is stopped, just as any other code
384- // that contains an error.
385- throw new ERR_INVALID_REPL_INPUT (
386- 'Listeners for `uncaughtException` cannot be used in the REPL' ) ;
387- }
388- } ) ;
389- addedNewListener = true ;
390401 }
391402
392- domainSet . add ( this . _domain ) ;
393-
394403 const savedRegExMatches = [ '' , '' , '' , '' , '' , '' , '' , '' , '' , '' ] ;
395404 const sep = '\u0000\u0000\u0000' ;
396405 const regExMatcher = new RegExp ( `^${ sep } (.*)${ sep } (.*)${ sep } (.*)${ sep } (.*)` +
@@ -612,13 +621,8 @@ function REPLServer(prompt,
612621 }
613622 } catch ( e ) {
614623 err = e ;
615-
616- if ( process . domain ) {
617- debug ( 'not recoverable, send to domain' ) ;
618- process . domain . emit ( 'error' , err ) ;
619- process . domain . exit ( ) ;
620- return ;
621- }
624+ self . _onEvalError ( e ) ;
625+ return ;
622626 }
623627
624628 if ( awaitPromise && ! err ) {
@@ -644,10 +648,8 @@ function REPLServer(prompt,
644648 const result = ( await promise ) ?. value ;
645649 finishExecution ( null , result ) ;
646650 } catch ( err ) {
647- if ( err && process . domain ) {
648- debug ( 'not recoverable, send to domain' ) ;
649- process . domain . emit ( 'error' , err ) ;
650- process . domain . exit ( ) ;
651+ if ( err ) {
652+ self . _onEvalError ( err ) ;
651653 return ;
652654 }
653655 finishExecution ( err ) ;
@@ -665,10 +667,17 @@ function REPLServer(prompt,
665667 }
666668 }
667669
668- self . eval = self . _domain . bind ( eval_ ) ;
670+ self . eval = function ( ...args ) {
671+ try {
672+ kActiveREPL = this ;
673+ FunctionPrototypeApply ( eval_ , this , args ) ;
674+ } catch ( e ) {
675+ self . _onEvalError ( e ) ;
676+ }
677+ } ;
669678
670- self . _domain . on ( 'error' , function debugDomainError ( e ) {
671- debug ( 'domain error' ) ;
679+ self . _onEvalError = function _onEvalError ( e ) {
680+ debug ( 'eval error' ) ;
672681 let errStack = '' ;
673682
674683 if ( typeof e === 'object' && e !== null ) {
@@ -696,11 +705,6 @@ function REPLServer(prompt,
696705 } ) ;
697706 decorateErrorStack ( e ) ;
698707
699- if ( e . domainThrown ) {
700- delete e . domain ;
701- delete e . domainThrown ;
702- }
703-
704708 if ( isError ( e ) ) {
705709 if ( e . stack ) {
706710 if ( e . name === 'SyntaxError' ) {
@@ -740,10 +744,13 @@ function REPLServer(prompt,
740744 self . lastError = e ;
741745 }
742746
743- if ( options [ kStandaloneREPL ] &&
744- process . listenerCount ( 'uncaughtException' ) !== 0 ) {
747+ if ( options [ kStandaloneREPL ] && process . listenerCount ( 'uncaughtException' ) > 1 ) {
745748 process . nextTick ( ( ) => {
746- process . emit ( 'uncaughtException' , e ) ;
749+ const listeners = process . listeners ( 'uncaughtException' ) ;
750+ for ( let i = 0 ; i < listeners . length ; i ++ ) {
751+ const listener = listeners [ i ] ;
752+ if ( listener !== handleUncaughtException ) listener ( e ) ;
753+ }
747754 self . clearBufferedCommand ( ) ;
748755 self . lines . level = [ ] ;
749756 self . displayPrompt ( ) ;
@@ -778,7 +785,14 @@ function REPLServer(prompt,
778785 self . lines . level = [ ] ;
779786 self . displayPrompt ( ) ;
780787 }
781- } ) ;
788+ } ;
789+ kListeningREPLs . add ( self ) ;
790+
791+ if ( ! kHasSetUncaughtListener ) {
792+ kAsyncHook . enable ( ) ;
793+ process . on ( 'uncaughtException' , handleUncaughtException ) ;
794+ kHasSetUncaughtListener = true ;
795+ }
782796
783797 self . clearBufferedCommand ( ) ;
784798
@@ -951,7 +965,7 @@ function REPLServer(prompt,
951965 self . displayPrompt ( ) ;
952966 return ;
953967 }
954- self . _domain . emit ( 'error' , e . err || e ) ;
968+ self . _onEvalError ( e . err || e ) ;
955969 }
956970
957971 // Clear buffer if no SyntaxErrors
@@ -971,8 +985,7 @@ function REPLServer(prompt,
971985 self . output . write ( self . writer ( ret ) + '\n' ) ;
972986 }
973987
974- // Display prompt again (unless we already did by emitting the 'error'
975- // event on the domain instance).
988+ // Display prompt again
976989 if ( ! e ) {
977990 self . displayPrompt ( ) ;
978991 }
@@ -1082,15 +1095,17 @@ REPLServer.prototype.clearBufferedCommand = function clearBufferedCommand() {
10821095REPLServer . prototype . close = function close ( ) {
10831096 if ( this . terminal && this . _flushing && ! this . _closingOnFlush ) {
10841097 this . _closingOnFlush = true ;
1085- this . once ( 'flushHistory' , ( ) =>
1086- ReflectApply ( Interface . prototype . close , this , [ ] ) ,
1087- ) ;
1098+ this . once ( 'flushHistory' , ( ) => {
1099+ removeListeningREPL ( this ) ;
1100+ ReflectApply ( Interface . prototype . close , this , [ ] ) ;
1101+ } ) ;
10881102
10891103 return ;
10901104 }
1091- process . nextTick ( ( ) =>
1092- ReflectApply ( Interface . prototype . close , this , [ ] ) ,
1093- ) ;
1105+ process . nextTick ( ( ) => {
1106+ removeListeningREPL ( this ) ;
1107+ ReflectApply ( Interface . prototype . close , this , [ ] ) ;
1108+ } ) ;
10941109} ;
10951110
10961111REPLServer . prototype . createContext = function ( ) {
0 commit comments