|
|
|
Escapes and breaks
Most programming language control structures (e.g., if-then-else) are
single-entry single-exit.
if E then C1 else C2
-------------------
| |
| true |
------> E ---> C1 ---------->
| | | |
| -----> C2 - |
| false |
| |
-------------------
while E do C
----------------------
| |
| false |
---------> E ------------------>
| | | |
| | ------> C |
| | true | |
| <----------- |
----------------------
It is "easy" to follow the flow-of-control when these
structures are "plugged-together" like tinker toys.
While all structured control constructs can be implemented with a disciplined
use of gotos, undisciplined use of goto's make reasoning about programs more
difficult because they allow multiple entry/exit from program blocks.
However, it is might be useful for the flow of control to jump out of
the expected course.
An escape is a sequencer that transfers control to the end of
the enclosing block. Useful primarily in loops.
while (not eof()) {
if (ch == 'q') break;
}
The control flow diagram is the following.
----------------------
| |
| false |
---------> E ------------------>
| | | | |
| | ------> C--------
| | true | |
| <----------- |
----------------------
The C break statement can only exit the containing block, so if we had
two nested loop with the break in the inner loop, it couldn't exit
from both loops.
In a function, a return can act as a general escape (leave the function now).
while (...) {
while (...) {
if (ch == 'q') return; /* exit both loops */
}
}
The most drastic of all is to "halt" the entire program.
In C, we can do this with an exit() function call.
while (...) {
while (...) {
if (error) exit(return_code); /* exit program */
}
}
Exceptions
An exception is an "exceptional" or unexpected condition.
Common exceptions include the following.
- arithmetic, e.g., division-by-zero
- arrays, e.g., out-of-bounds
- I/O, e.g., can't open file
An exception is said to be "thrown" if it is raised or signaled.
Some languages have no capabilities for handling exceptions.
What happens when we try to divide by zero in Pascal? In C?
x = 0;
z = y / x; /* error division by zero --> core dump */
Other languages provide exception handlers. PL/1 was the first major
language to provide exception handlers.
In languages with exception handlers, there are several important issues
to consider.
-
Which handler is called when an exception is thrown? Said differently,
how is an exception handler bound or associated with an exception?
-
When an exception handler finishes, where does the program continue
execution? This is known as continuation. Exceution could
continue at the statement following the statement that threw the
exception, at the end of the block enclosing that statement, at the
end of the subprogram containing the exception, or some other place.
-
What information can be passed in an exception to the handler?
-
Can exceptions be propagated, that is, if there is no local
handler, can the exception be thrown "up the stack" to other
handlers?
Java provides exception handlers with the try-catch statement.
try {
x = 0;
z = y / x; // continuation is at the end of the try
x = z;
} catch (DivideByZeroException e) {
System.out.println("Warning, tried to divide-by-zero, am continuing");
z = 0;
}
...
Java has the following model for exceptions.
-
An exception is an object. It is an instance of a class that is
an descendent of the Exception class (in the inheritance hierarchy).
So to create a DivideByZeroException object, we would first need to
have a DivideByZeroException class that extends Exception.
There are a few built-in Exception classes.
A try statement may have several catch clauses. The catches are
tried in sequence until one "catches". An exception is caught by
a catch that matches the Exception (or is an ancestor). So if
DivideByZeroException extends Exception, then a DivideByZeroException
would be caught by either DivideByZeroException or Exception, but not
by any other Exception, e.g., ArrayOutOfBoundsException.
-
If an exception is caught, continuation happens at the end of the try.
-
An Exception is an object, the constructor is called when the
Exception is created and thrown. So all kinds of information can
be passed.
-
A method can throw an Exception that is not caught within the method.
Exceptions are especially nice in I/O. Consider the standard act of
opening a file and reading data into memory. There are lots of
exceptions (or errors) to handle.
readFile {
try {
open the file;
determine its size;
allocate that much memory;
read the file into memory;
close the file;
} catch (fileOpenFailed) {
doSomething;
} catch (sizeDeterminationFailed) {
doSomething;
} catch (memoryAllocationFailed) {
doSomething;
} catch (readFailed) {
doSomething;
} catch (fileCloseFailed) {
doSomething;
}
}
Note that the catch statements scans for particular kinds of errors.
The alternative is to write code like this.
errorCodeType readFile {
initialize errorCode = 0;
open the file;
if (theFileIsOpen) {
determine the length of the file;
if (gotTheFileLength) {
allocate that much memory;
if (gotEnoughMemory) {
read the file into memory;
if (readFailed) {
errorCode = -1;
}
} else {
errorCode = -2;
}
} else {
errorCode = -3;
}
close the file;
if (theFileDidntClose && errorCode == 0) {
errorCode = -4;
} else {
errorCode = errorCode and -4;
}
} else {
errorCode = -5;
}
return errorCode;
}
But this code is very bloated, and it is unclear as to what is actually
going on.
Propagating Exceptions
What happens if we call a method inside of a try statement, but detect
errors in that method?
A method may propagate exceptions to callees (up the call stack).
method1 {
try {
call method2;
} catch (Exception2 e) {
doErrorProcessing;
}
}
method2 throws Exception2 {
call method3;
}
method3 throws Exception3 {
call readFile;
}
Source of Information
These lecture notes are based on chapter 8 in
"Programming Language Concepts and Paradigms" by David Watt,
Brewing Java, A Tutorial, and Chapter 14 in
"Programming Languages, 6ed" by Robert Sebesta.
|