@@ -534,8 +534,26 @@ type RubyVMPrivate = {
534534
535535class RbExceptionFormatter {
536536 private literalsCache : [ RbValue , RbValue , RbValue ] | null = null ;
537+ private isFormmatting : boolean = false ;
537538
538539 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 {
539557 const [ zeroLiteral , oneLiteral , newLineLiteral ] = ( ( ) => {
540558 if ( this . literalsCache == null ) {
541559 const zeroOneNewLine : [ RbValue , RbValue , RbValue ] = [
@@ -550,21 +568,42 @@ class RbExceptionFormatter {
550568 }
551569 } ) ( ) ;
552570
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+
554592 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 ) ;
559606 }
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- ] ) ;
568607 }
569608
570609 formatString (
0 commit comments