Previous Page | Next Page

Handling Exceptions

Handling SCL Exceptions with CATCH and THROW

All exceptions are subclasses of the SCL Exception class, which is a subclass of the SCL Throwable class. You can use the CLASS statement to define your own exception classes, and then use the THROW and CATCH statements to handle the exception.

Because an exception is a class, you can design the class to contain any information that is relevant to recovering from the specific exception. A simple exception class may contain only an error message. For example, the following class defines a subclass of SCLException called NewException, which defines an error message string named SecondaryMessage:

NewException Class

Class NewException extends SCLException
   dcl string SecondaryMessage;
endclass;

You can then create a new instance of NewException and raise this exception with the THROW statement,as shown in the ThrowIt class:

ThrowIt Class

Class ThrowIt;
  m: method;
  dcl NewException NE = _new_ NewException('Exception in method m');
  NE.SecondaryMessage = "There's no code in m!";
  throw NE;
  endmethod;
endclass;

Note:   You must always declare a variable to hold the thrown exception.  [cautionend]

The code that processes the exception is enclosed in CATCH blocks. CATCH blocks can contain any code needed to process the exception, including more CATCH and THROW statements.

When an exception is thrown, normal execution of the entry stops, and SCL begins looking for a CATCH block to process the thrown class. The CATCH block can contain any statements needed to process the exception. For example, the following code prints the stack traceback at the point of the throw.

do;
dcl NewException NE = _new_ NewException('Exception in method m'); 
NE.SecondaryMessage = "There's no code in m!"; 
throw NE; 

catch NE;
   put NE.getMessage();         /* Print exception information. */
   call putlist(NE.traceback);
   put NE.SecondaryMessage=;    /* Print secondary message. */
endcatch;
end;

Note:   CATCH blocks must always be enclosed in DO statements.  [cautionend]

The traceback information that is printed by this example is stored automatically by SCL when an exception is thrown. See The SCL Throwable and SCL Exception Classes for more information.

Note:   When a CATCH block has finished executing, control transfers to the end of the current DO statement, and the program resumes normal execution. If no exception has been thrown and SCL encounters a CATCH block, control transfers to the end of the current DO statement and execution resumes at that location. Therefore, any SCL statements the occur between CATCH blocks or following the last CATCH block within the same DO group will never be executed. Any SCL statements within the DO group that are not part of a CATCH block but must execute must be entered at the beginning of the DO group.  [cautionend]

After an exception is processed, program execution continues normally.


Example

Suppose you have the following class Y. This class defines a method called update that throws an exception that is an instance of the SCL Exception class.

import sashelp.classes;
class Y;
update: method;
   if (_self_.readOnly) then
      /* Throw an exception.  Set message via constructor. */
      throw _new_ SCLException('Cannot update when in ready-only mode');
endmethod;
endclass;

Class X defines method M, which declares a local variable to hold the exception, and then calls the update method, which throws the exception. The exception is then processed by the CATCH block for SCLE.

import sashelp.classes;
class X;
M: method;
   do;
   /* Declare the local exception variable. */
   dcl SCLException scle;
   dcl Y y = _new_ y();
   
   /* Call update method, which throws SCLEception. */
   y.update();

   /* Process the SCLException. */
   catch scle;
      /* Print exception information. */
      put scle.getMessage();
      call putlist(scle.traceback);
   endcatch;
   end;
endmethod;
endclass;


How SCL Determines Which CATCH Block To Execute

SCL uses the scope of the DO group that contains the CATCH block and the class of the exception to determine which CATCH block to execute.

Suppose that in addition to the NewException class (see NewException Class) you define a subclass of NewException called SubException:

NewException Class

Class NewException extends SCLException
   dcl string SecondaryMessage;
endclass;

SubException Class

Class SubException extends NewException
   ...code to process SubExceptions...
endclass;

As with all exceptions, SCL first searches the current DO group for a CATCH block that is related to the thrown class. In this example, because NEsub is an instance of SubException and SubException is a subclass of NewException, SCL will execute the CATCH block for NE because it is in the scope of the current DO group. The CATCH block for NEsub is in a different scope (the outer DO group), so it will not be executed unless the CATCH block for NE is modified to rethrow (see Catching and Rethrowing Exceptions) the exception. If the CATCH block for NE rethrows the exception, then both CATCH blocks will be executed.

Nested DO Statements

dcl NewException NE;
dcl SubException NEsub;

do;
   do;
   NEsub = _new_ SubException('Exception in method m'); 
   NEsub.SecondaryMessage = "There's no code in m!"; 
   throw NEsub; 

   catch NE;
      put NE.getMessage();         /* Print exception information. */
      call putlist(NE.traceback);       
      put NE.SecondaryMessage=;    /* Print secondary message. */
      /* Could rethrow the NEsub exception if needed. */
   endcatch; 
   end; 

/* The following CATCH block will not be executed */
/* unless the CATCH block for NE rethrows the exception. */

catch NEsub;
   ...code to process NEsub exceptions...
endcatch;
end;


Catching and Rethrowing Exceptions

Each entry in the stack can process an exception and then pass it back up the stack by rethrowing it, which allows the calling entry to perform additional processing. Each entry can perform whatever processing is relevant to that entry.

do;
catch e1;
   ...process the exception...
   throw e1;  /* Rethrow the exception. */
endcatch;
end;

Note:   If an exception is rethrown within a CATCH block, no CATCH block within the same scope can recatch the exception. The exception is passed out of the scope where it was thrown.  [cautionend]

If SCL finds a second CATCH block for E1 within the same SCL entry but outside of the scope of the DO group where the exception was thrown, then execution continues with that second CATCH block. If SCL does not find another CATCH block for E1 in that same SCL entry, then the exception is passed up the stack to the calling entry.

Suppose you have defined the NewException class (see NewException Class) and the ThrowIt class (see ThrowIt Class). The following program section calls method M, which throws the exception NE. The two CATCH blocks catch, rethrow, and recatch the exception.

init:
dcl ThrowIt TI = _new_ThrowIt();
dcl NewException NE;
do;
   do;
   TI.m();

   catch NE;
      put 'caught it';
      throw NE;
   endcatch;
   end;

catch NE;
   put 'caught it again';
endcatch;
end;
return;

Note:   You cannot define multiple CATCH blocks for the same exception within the same scope.  [cautionend]


Nested CATCH Blocks

You can nest CATCH blocks. For example, suppose you define the class W as follows:

class w;
m: method n:num;
do;
dcl e1 e1;
dcl e2 e2;
   do;
   if (n < 0) then throw _new_ e2();
      else throw _new_ e1();
   catch e2;
      put 'caught inner e2';
      do;
      dcl e1 e1;
      if (n<0) then throw _new_ e2();
         else throw _new_ e1();
      catch e1;
         put 'caught inner e1';
      endcatch;
      end;
   endcatch;
   end;
   catch e1;
      put 'caught outer e1';
   endcatch;

   catch e2;
      put 'caught outer e2';
   endcatch;
end;
endmethod;
endclass;
  

If you invoke method M with a negative argument as in the following program section:

init:
dcl w w = _new_ w();
w.m(-2);
return;

then the output would be

caught inner e2
caught outer e2


The SCL Throwable and SCL Exception Classes

All exceptions are subclasses of the SCL Exception class, which is a subclass of the SCL Throwable class. When an exception is thrown, SCL automatically stores the name of the entry that throws the exception, the line number where the throw occurs, and the stack traceback at the point of the throw. You can set the message attribute via the constructor when an instance of the exception is created. You can use the getMessage method to return the message.

SCL Throwable Class

class SCLThrowable;
   public string(32767) message;
   public list traceback;  /* stack traceback */
   public string entry;    /* SCL entry name */
   public num line;        /* line number */

   SCLThrowable: public method s:string;
      message = s;
   endmethod;

   getMessage: public method return=string;
      return message;
   endmethod;
endclass;

SCL Exception Class

class SCLException extends SCLThrowable;
   SCLException: public method /(state='o');
      _super("SCLException");
   endmethod;

   SCLException: public method s:string /(state='o');
      _super(s);
   endmethod;
endclass;

Previous Page | Next Page | Top of Page