Book Contents Previous Chapter Next Chapter
This chapter describes Magic Cap's features for handling exceptions, serious errors that arise while Magic Cap and its packages are running. Before reading this chapter, you should be familiar with Magic Cap's object runtime and the basic features of software packages.
Magic Cap provides features for handling exceptions, serious errors that can occur in your packages or in Magic Cap itself. When Magic Cap encounters a serious error, such as a critical shortage of memory, a hardware failure, or a needed object that is missing, it throws an exception; that is, it causes an exception to occur. You can also define and throw exceptions in your packages to indicate when various errors occur.
When an exception is thrown, Magic Cap calls a corresponding exception handler to perform some action for processing the error indicated by the exception. You can create your own exception handlers to process exceptions thrown by your package or by Magic Cap itself. In your exception handler, you can take action to recover from the error. You can then signal that the exception has been handled, or you can allow another exception handler to perform further recovery from the error.
When you create an exception handler, Magic Cap saves the state of various system values. Magic Cap maintains a stack of these saved states. When an exception is thrown, Magic Cap restores the state at the top of this stack and calls the associated exception handler. When you create a new exception handler, its associated state goes on the top of this stack, and when an exception handler is called, its state is removed from the stack. This allows exception handlers to pass exceptions through to other handlers.
This section describes how you can use Magic Cap to create exception handlers and work with exceptions in other ways.
Magic Cap defines class Exceptions to use when defining exception handlers. Class Exceptions has no fields, and you'll never create any objects of this class. Class Exceptions exists only to provide a home for the intrinsics that allow you to work with exceptions.
Magic Cap provides two schemes for creating exception handlers. The first scheme, which uses the Try operation, lets you specify code as your exception handler when you perform some action that may cause an exception. If the code causes an exception, your exception handler is called.
The second scheme for exception handling, which uses the Catch operation, lets you specify an exception handler that will only be called for a particular exception. If your code causes that exception, your exception handler is called.
You can use the Try operation when performing a risky action that might cause an exception. If you call Try before performing the risky operation, your exception handling code will be called if an exception is thrown.
When you call Try, Magic Cap saves the state of the current actor, including the program counter, stack, and CPU registers, then continues execution. Subsequently, if your risky code throws an exception, Magic Cap restores the state to the saved values, effectively "rewinding" your package to the Try call and executing it again.
When Magic Cap first executes your Try operation, it returns nilObject as the result of Try, then executes the risky code. If this code throws an exception, Magic Cap restores the state of the actor, thus calling Try again. This time, Try returns the exception as the result.
Your code should test the return value of Try. If Try returns nilObject, you can assume that it is being executed for the first time, and you should skip over your exception handling code. If Try returns a non-nil value, an exception has been thrown and you should handle it. When your exception handler is executed, the saved state associated with it is removed from the stack of saved states.
You can use the following structure for your code when you call Try:
ObjectID exception; // When this Try executes the first time, Magic Cap // records the actor's state and Try returns nilObject. if ((exception = Try()) == nilObject) { // Risky code that might throw an exception, either directly // or indirectly, goes here. If an exception is thrown, // Magic Cap restores the state and executes the above Try // again, this time returning the exception. // Call Commit at the end of the risky code. If Commit // is reached, the risky code didn't cause an exception, // so the state recorded by the matching Try is discarded. Commit(); } else { // If an exception was thrown by the code above, the // if statement will cause this code to run. This is // the exception handler. It should recover from the // exception as much as possible. Optionally, it can then // call Fail to throw the exception to the next-highest // handler (see below). }
You should call Commit after completing the risky code. Commit indicates that the risky code has not failed and removes the corresponding saved state.
Your exception handler can examine the value returned by Try to find out which exception was thrown. CodeWarrior Magic defines constants for exceptions, which you can see in the file Exceptions.h. The exception handler can deal with exceptions thrown directly by your code, or indirectly by Magic Cap operations called by your code.
If you can't handle the exception, you can call Fail to invoke the next exception handler. When the next exception handler is executed, its state will be removed from the stack of saved states.
If an exception is thrown without a handler to catch it, Magic Cap restarts and shows the cleaning up window. If an uncaught exception occurs in a debugging version of Magic Cap, such as Magic Cap Simulator, the uncaught exception causes Magic Cap to break into the debugger.
The code structure shown above is suggested, but not required, when calling Try. For example, you can also use the following alternate structure:
if (Try() != nilObject) { // The exception handler goes here when using // this structure. This code should include some // way to avoid executing the code that follows, // such as a return statement to exit the function. return; } // The code that might throw an exception is next, // followed by Commit. Commit();
If you have risky code that might throw a particular exception, you can use the Catch operation to set up an exception handler that will be called only if that exception is thrown. This exception handler can deal with the particular exception you specify, whether it is thrown by your code or by Magic Cap. When you call Catch, you'll use the following structure for your code:
if (Catch(particularException) == nilObject) { // Risky code that might throw the given exception, // either directly or indirectly, goes here. // Call Commit at the end of the risky code. This removes // the saved state associated with the exception handler. Commit(); } else { // If the given exception is thrown by the code above, // Magic Cap calls here. This code should recover // from the exception as much as possible. Optionally, it // can then call Fail to throw the exception to the next // handler. }
If you call Catch to create an exception handler and an exception is thrown, Magic Cap restores the execution state that was present before the Catch, as with Try. As with Try, the code structure shown above for calling Catch is suggested, but not required.
If an exception is thrown without a handler to catch it, Magic Cap restarts and shows the cleaning up window. If an uncaught exception occurs in a debugging version of Magic Cap, such as Magic Cap Simulator, the uncaught exception causes Magic Cap to break into the debugger.
For more information, see the Exceptions sample package and these topics in Magic Cap Class and Method Reference:
class Exceptions
operations and attributes:
Catch Commit Fail Try
Book Contents Previous Chapter Next Chapter