@@ -534,8 +534,26 @@ type RubyVMPrivate = {
534
534
535
535
class RbExceptionFormatter {
536
536
private literalsCache : [ RbValue , RbValue , RbValue ] | null = null ;
537
+ private isFormmatting : boolean = false ;
537
538
538
539
format ( error : RbValue , vm : RubyVM , privateObject : RubyVMPrivate ) : string {
540
+ // All Ruby exceptions raised during formatting exception message should
541
+ // be caught and return a fallback message.
542
+ // Therefore, we don't need to worry about infinite recursion here ideally
543
+ // but checking re-entrancy just in case.
544
+ class RbExceptionFormatterError extends Error { }
545
+ if ( this . isFormmatting ) {
546
+ throw new RbExceptionFormatterError ( "Unexpected exception occurred during formatting exception message" ) ;
547
+ }
548
+ this . isFormmatting = true ;
549
+ try {
550
+ return this . _format ( error , vm , privateObject ) ;
551
+ } finally {
552
+ this . isFormmatting = false ;
553
+ }
554
+ }
555
+
556
+ private _format ( error : RbValue , vm : RubyVM , privateObject : RubyVMPrivate ) : string {
539
557
const [ zeroLiteral , oneLiteral , newLineLiteral ] = ( ( ) => {
540
558
if ( this . literalsCache == null ) {
541
559
const zeroOneNewLine : [ RbValue , RbValue , RbValue ] = [
@@ -550,21 +568,42 @@ class RbExceptionFormatter {
550
568
}
551
569
} ) ( ) ;
552
570
553
- const backtrace = error . call ( "backtrace" ) ;
571
+ let className : string ;
572
+ let backtrace : RbValue ;
573
+ let message : string ;
574
+ try {
575
+ className = error . call ( "class" ) . toString ( ) ;
576
+ } catch ( e ) {
577
+ className = "unknown" ;
578
+ }
579
+
580
+ try {
581
+ message = error . toString ( ) ;
582
+ } catch ( e ) {
583
+ message = "unknown" ;
584
+ }
585
+
586
+ try {
587
+ backtrace = error . call ( "backtrace" ) ;
588
+ } catch ( e ) {
589
+ return this . formatString ( className , message ) ;
590
+ }
591
+
554
592
if ( backtrace . call ( "nil?" ) . toString ( ) === "true" ) {
555
- return this . formatString (
556
- error . call ( "class" ) . toString ( ) ,
557
- error . toString ( ) ,
558
- ) ;
593
+ return this . formatString ( className , message ) ;
594
+ }
595
+ try {
596
+ const firstLine = backtrace . call ( "at" , zeroLiteral ) ;
597
+ const restLines = backtrace
598
+ . call ( "drop" , oneLiteral )
599
+ . call ( "join" , newLineLiteral ) ;
600
+ return this . formatString ( className , message , [
601
+ firstLine . toString ( ) ,
602
+ restLines . toString ( ) ,
603
+ ] ) ;
604
+ } catch ( e ) {
605
+ return this . formatString ( className , message ) ;
559
606
}
560
- const firstLine = backtrace . call ( "at" , zeroLiteral ) ;
561
- const restLines = backtrace
562
- . call ( "drop" , oneLiteral )
563
- . call ( "join" , newLineLiteral ) ;
564
- return this . formatString ( error . call ( "class" ) . toString ( ) , error . toString ( ) , [
565
- firstLine . toString ( ) ,
566
- restLines . toString ( ) ,
567
- ] ) ;
568
607
}
569
608
570
609
formatString (
0 commit comments