2424const internalModule = require ( 'internal/module' ) ;
2525const internalUtil = require ( 'internal/util' ) ;
2626const util = require ( 'util' ) ;
27+ const utilBinding = process . binding ( 'util' ) ;
2728const inherits = util . inherits ;
2829const Stream = require ( 'stream' ) ;
2930const vm = require ( 'vm' ) ;
@@ -178,7 +179,7 @@ function REPLServer(prompt,
178179replMode ) ;
179180}
180181
181- var options , input , output , dom ;
182+ var options , input , output , dom , breakEvalOnSigint ;
182183if ( prompt !== null && typeof prompt === 'object' ) {
183184// an options object was given
184185options = prompt ;
@@ -191,10 +192,17 @@ function REPLServer(prompt,
191192prompt = options . prompt ;
192193dom = options . domain ;
193194replMode = options . replMode ;
195+ breakEvalOnSigint = options . breakEvalOnSigint ;
194196} else {
195197options = { } ;
196198}
197199
200+ if ( breakEvalOnSigint && eval_ ) {
201+ // Allowing this would not reflect user expectations.
202+ // breakEvalOnSigint affects only the behaviour of the default eval().
203+ throw new Error ( 'Cannot specify both breakEvalOnSigint and eval for REPL' ) ;
204+ }
205+
198206var self = this ;
199207
200208self . _domain = dom || domain . create ( ) ;
@@ -204,6 +212,7 @@ function REPLServer(prompt,
204212self . replMode = replMode || exports . REPL_MODE_SLOPPY ;
205213self . underscoreAssigned = false ;
206214self . last = undefined ;
215+ self . breakEvalOnSigint = ! ! breakEvalOnSigint ;
207216
208217self . _inTemplateLiteral = false ;
209218
@@ -267,14 +276,46 @@ function REPLServer(prompt,
267276regExMatcher . test ( savedRegExMatches . join ( sep ) ) ;
268277
269278if ( ! err ) {
279+ // Unset raw mode during evaluation so that Ctrl+C raises a signal.
280+ let previouslyInRawMode ;
281+ if ( self . breakEvalOnSigint ) {
282+ // Start the SIGINT watchdog before entering raw mode so that a very
283+ // quick Ctrl+C doesn’t lead to aborting the process completely.
284+ utilBinding . startSigintWatchdog ( ) ;
285+ previouslyInRawMode = self . _setRawMode ( false ) ;
286+ }
287+
270288try {
271- if ( self . useGlobal ) {
272- result = script . runInThisContext ( { displayErrors : false } ) ;
273- } else {
274- result = script . runInContext ( context , { displayErrors : false } ) ;
289+ try {
290+ const scriptOptions = {
291+ displayErrors : false ,
292+ breakOnSigint : self . breakEvalOnSigint
293+ } ;
294+
295+ if ( self . useGlobal ) {
296+ result = script . runInThisContext ( scriptOptions ) ;
297+ } else {
298+ result = script . runInContext ( context , scriptOptions ) ;
299+ }
300+ } finally {
301+ if ( self . breakEvalOnSigint ) {
302+ // Reset terminal mode to its previous value.
303+ self . _setRawMode ( previouslyInRawMode ) ;
304+
305+ // Returns true if there were pending SIGINTs *after* the script
306+ // has terminated without being interrupted itself.
307+ if ( utilBinding . stopSigintWatchdog ( ) ) {
308+ self . emit ( 'SIGINT' ) ;
309+ }
310+ }
275311}
276312} catch ( e ) {
277313err = e ;
314+ if ( err . message === 'Script execution interrupted.' ) {
315+ // The stack trace for this case is not very useful anyway.
316+ Object . defineProperty ( err , 'stack' , { value : '' } ) ;
317+ }
318+
278319if ( err && process . domain ) {
279320debug ( 'not recoverable, send to domain' ) ;
280321process . domain . emit ( 'error' , err ) ;
0 commit comments