@@ -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,
@@ -118,6 +119,7 @@ const { inspect } = require('internal/util/inspect');
118119const vm = require ( 'vm' ) ;
119120
120121const { runInThisContext, runInContext } = vm . Script . prototype ;
122+ const { setUncaughtExceptionCaptureCallback } = process ;
121123
122124const path = require ( 'path' ) ;
123125const fs = require ( 'fs' ) ;
@@ -138,7 +140,6 @@ ArrayPrototypeForEach(
138140 BuiltinModule . getSchemeOnlyModuleNames ( ) ,
139141 ( lib ) => ArrayPrototypePush ( nodeSchemeBuiltinLibs , `node:${ lib } ` ) ,
140142) ;
141- const domain = require ( 'domain' ) ;
142143let debug = require ( 'internal/util/debuglog' ) . debuglog ( 'repl' , ( fn ) => {
143144 debug = fn ;
144145} ) ;
@@ -191,6 +192,7 @@ const {
191192const {
192193 makeContextifyScript,
193194} = require ( 'internal/vm' ) ;
195+ const { createHook } = require ( 'async_hooks' ) ;
194196let nextREPLResourceNumber = 1 ;
195197// This prevents v8 code cache from getting confused and using a different
196198// cache from a resource of the same name
@@ -205,13 +207,44 @@ const globalBuiltins =
205207 new SafeSet ( vm . runInNewContext ( 'Object.getOwnPropertyNames(globalThis)' ) ) ;
206208
207209const parentModule = module ;
208- const domainSet = new SafeWeakSet ( ) ;
209210
210211const kBufferedCommandSymbol = Symbol ( 'bufferedCommand' ) ;
211212const kContextId = Symbol ( 'contextId' ) ;
212213const kLoadingSymbol = Symbol ( 'loading' ) ;
214+ const kListeningREPLs = new SafeSet ( ) ;
215+ const kAsyncREPLMap = new SafeMap ( ) ;
216+ let kActiveREPL ;
217+ const kAsyncHook = createHook ( {
218+ init ( asyncId ) {
219+ if ( kActiveREPL ) {
220+ kAsyncREPLMap . set ( asyncId , kActiveREPL ) ;
221+ }
222+ } ,
223+
224+ before ( asyncId ) {
225+ kActiveREPL = kAsyncREPLMap . get ( asyncId ) || kActiveREPL ;
226+ } ,
213227
214- let addedNewListener = false ;
228+ destroy ( asyncId ) {
229+ kAsyncREPLMap . delete ( asyncId ) ;
230+ } ,
231+ } ) ;
232+
233+ let kHasSetUncaughtListener = false ;
234+
235+ function handleUncaughtException ( er ) {
236+ kActiveREPL ?. _onEvalError ( er ) ;
237+ }
238+
239+ function removeListeningREPL ( repl ) {
240+ kListeningREPLs . delete ( repl ) ;
241+ if ( kListeningREPLs . size === 0 ) {
242+ kAsyncHook . disable ( ) ;
243+ kHasSetUncaughtListener = false ;
244+ setUncaughtExceptionCaptureCallback ( null ) ;
245+ process . setUncaughtExceptionCaptureCallback = setUncaughtExceptionCaptureCallback ;
246+ }
247+ }
215248
216249try {
217250 // Hack for require.resolve("./relative") to work properly.
@@ -347,7 +380,6 @@ function REPLServer(prompt,
347380
348381 this . allowBlockingCompletions = ! ! options . allowBlockingCompletions ;
349382 this . useColors = ! ! options . useColors ;
350- this . _domain = options . domain || domain . create ( ) ;
351383 this . useGlobal = ! ! useGlobal ;
352384 this . ignoreUndefined = ! ! ignoreUndefined ;
353385 this . replMode = replMode || module . exports . REPL_MODE_SLOPPY ;
@@ -370,28 +402,8 @@ function REPLServer(prompt,
370402 // It is possible to introspect the running REPL accessing this variable
371403 // from inside the REPL. This is useful for anyone working on the REPL.
372404 module . exports . repl = this ;
373- } else if ( ! addedNewListener ) {
374- // Add this listener only once and use a WeakSet that contains the REPLs
375- // domains. Otherwise we'd have to add a single listener to each REPL
376- // instance and that could trigger the `MaxListenersExceededWarning`.
377- process . prependListener ( 'newListener' , ( event , listener ) => {
378- if ( event === 'uncaughtException' &&
379- process . domain &&
380- listener . name !== 'domainUncaughtExceptionClear' &&
381- domainSet . has ( process . domain ) ) {
382- // Throw an error so that the event will not be added and the current
383- // domain takes over. That way the user is notified about the error
384- // and the current code evaluation is stopped, just as any other code
385- // that contains an error.
386- throw new ERR_INVALID_REPL_INPUT (
387- 'Listeners for `uncaughtException` cannot be used in the REPL' ) ;
388- }
389- } ) ;
390- addedNewListener = true ;
391405 }
392406
393- domainSet . add ( this . _domain ) ;
394-
395407 const savedRegExMatches = [ '' , '' , '' , '' , '' , '' , '' , '' , '' , '' ] ;
396408 const sep = '\u0000\u0000\u0000' ;
397409 const regExMatcher = new RegExp ( `^${ sep } (.*)${ sep } (.*)${ sep } (.*)${ sep } (.*)` +
@@ -613,13 +625,8 @@ function REPLServer(prompt,
613625 }
614626 } catch ( e ) {
615627 err = e ;
616-
617- if ( process . domain ) {
618- debug ( 'not recoverable, send to domain' ) ;
619- process . domain . emit ( 'error' , err ) ;
620- process . domain . exit ( ) ;
621- return ;
622- }
628+ self . _onEvalError ( e ) ;
629+ return ;
623630 }
624631
625632 if ( awaitPromise && ! err ) {
@@ -645,10 +652,8 @@ function REPLServer(prompt,
645652 const result = ( await promise ) ?. value ;
646653 finishExecution ( null , result ) ;
647654 } catch ( err ) {
648- if ( err && process . domain ) {
649- debug ( 'not recoverable, send to domain' ) ;
650- process . domain . emit ( 'error' , err ) ;
651- process . domain . exit ( ) ;
655+ if ( err ) {
656+ self . _onEvalError ( err ) ;
652657 return ;
653658 }
654659 finishExecution ( err ) ;
@@ -666,10 +671,17 @@ function REPLServer(prompt,
666671 }
667672 }
668673
669- self . eval = self . _domain . bind ( eval_ ) ;
674+ self . eval = function ( ...args ) {
675+ try {
676+ kActiveREPL = this ;
677+ FunctionPrototypeApply ( eval_ , this , args ) ;
678+ } catch ( e ) {
679+ self . _onEvalError ( e ) ;
680+ }
681+ } ;
670682
671- self . _domain . on ( 'error' , function debugDomainError ( e ) {
672- debug ( 'domain error' ) ;
683+ self . _onEvalError = function _onEvalError ( e ) {
684+ debug ( 'eval error' ) ;
673685 let errStack = '' ;
674686
675687 if ( typeof e === 'object' && e !== null ) {
@@ -697,11 +709,6 @@ function REPLServer(prompt,
697709 } ) ;
698710 decorateErrorStack ( e ) ;
699711
700- if ( e . domainThrown ) {
701- delete e . domain ;
702- delete e . domainThrown ;
703- }
704-
705712 if ( isError ( e ) ) {
706713 if ( e . stack ) {
707714 if ( e . name === 'SyntaxError' ) {
@@ -779,7 +786,20 @@ function REPLServer(prompt,
779786 self . lines . level = [ ] ;
780787 self . displayPrompt ( ) ;
781788 }
782- } ) ;
789+ } ;
790+ kListeningREPLs . add ( self ) ;
791+
792+ if ( ! kHasSetUncaughtListener ) {
793+ kAsyncHook . enable ( ) ;
794+ process . setUncaughtExceptionCaptureCallback = ( ) => {
795+ throw new ERR_INVALID_REPL_INPUT ( ) ;
796+ } ;
797+ // Set to null first to prevent ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET from
798+ // being thrown on set.
799+ setUncaughtExceptionCaptureCallback ( null ) ;
800+ setUncaughtExceptionCaptureCallback ( handleUncaughtException ) ;
801+ kHasSetUncaughtListener = true ;
802+ }
783803
784804 self . clearBufferedCommand ( ) ;
785805
@@ -952,7 +972,7 @@ function REPLServer(prompt,
952972 self . displayPrompt ( ) ;
953973 return ;
954974 }
955- self . _domain . emit ( 'error' , e . err || e ) ;
975+ self . _onEvalError ( e . err || e ) ;
956976 }
957977
958978 // Clear buffer if no SyntaxErrors
@@ -972,8 +992,7 @@ function REPLServer(prompt,
972992 self . output . write ( self . writer ( ret ) + '\n' ) ;
973993 }
974994
975- // Display prompt again (unless we already did by emitting the 'error'
976- // event on the domain instance).
995+ // Display prompt again
977996 if ( ! e ) {
978997 self . displayPrompt ( ) ;
979998 }
@@ -1083,15 +1102,17 @@ REPLServer.prototype.clearBufferedCommand = function clearBufferedCommand() {
10831102REPLServer . prototype . close = function close ( ) {
10841103 if ( this . terminal && this . _flushing && ! this . _closingOnFlush ) {
10851104 this . _closingOnFlush = true ;
1086- this . once ( 'flushHistory' , ( ) =>
1087- ReflectApply ( Interface . prototype . close , this , [ ] ) ,
1088- ) ;
1105+ this . once ( 'flushHistory' , ( ) => {
1106+ removeListeningREPL ( this ) ;
1107+ ReflectApply ( Interface . prototype . close , this , [ ] ) ;
1108+ } ) ;
10891109
10901110 return ;
10911111 }
1092- process . nextTick ( ( ) =>
1093- ReflectApply ( Interface . prototype . close , this , [ ] ) ,
1094- ) ;
1112+ process . nextTick ( ( ) => {
1113+ removeListeningREPL ( this ) ;
1114+ ReflectApply ( Interface . prototype . close , this , [ ] ) ;
1115+ } ) ;
10951116} ;
10961117
10971118REPLServer . prototype . createContext = function ( ) {
0 commit comments