Bistro Exceptions

While some superficial similarities exist between Smalltalk and Java™ exception handling mechanisms, there are also some fundamental differences. So, deciding whether and how to integrate these mechanisms presented one of the more challenging problems for the design of Bistro. The following table compares the salient aspects of the Smalltalk and Java™ exception handling mechanisms.

Table 7. Smalltalk and Java™ Exceptions
Smalltalk models exceptions as instances of exception classes (Exception and its subclasses).
Java™ models exceptions as instances of exception classes (Throwable and its subclasses).
   
Smalltalk has no special syntax for dealing with exceptions, only standard message idioms.
Java™ has syntax for dealing with exceptions: try { } catch { } finally { }, throw and throws.
   
Smalltalk provides fine grained control over exception handling, including the ability to decide whether and when stack frames are unwound, and the ability to resume execution at the point of origin (where the exception was raised).
Java™ exception handling is strictly stack oriented - once unwound, the stack frames between the exception origin (throw) and the handler (catch) are unavailable.
   
Smalltalk exceptions never impact method signatures.
Java™ exceptions thrown within a method must be declared in a throws clause in the method signature, except for those exceptions whose classes are derived from java.lang.RuntimeException.

Finding the common ground between Smalltalk and Java™ exceptions is straightforward, but merging the mechanisms completely does not really make sense. Some of the Smalltalk idioms can easily be mapped to Java™. For example, the ZeroArgumentBlock class provides the standard ifCurtailed:, ensure:, and on:do: methods for handling exceptions. However, Bistro also provides a new idiom that the compiler maps directly to the Java™ exception handling mechanism.

Bistro Exception Idioms
[ "..." ] ifCurtailed:
[ "... catch any exception ..." ].
The ifCurtailed: method evaluates a block, and if an exception throws control out of the block, the exception block will be evaluated. This idiom is useful for catching all exceptions.
[ "..." ] ensure:
[ "... evaluated finally ..." ].
The ensure: method evaluates a block and guarantees that the final block will be executed, even if an exception throws control out of the first block. This idiom is useful for ensuring that resources are released properly even under exceptional circumstances.
[ "..." ] on: Exception do: [ "..." ].
[ "..." ] on: Exception do:
[ :exception | "..." ].
The on:do: method evaluates a block, and if an exception throws control out of the block, the exception block will be evaluated (with the exception as an argument if the second code pattern is used).
[ "..." ]
catch: [ :exception (ExceptionClass) | "..." ]
"... additional catch blocks ..."
ensure: [ "... evaluated finally ..." ].
This new message idiom allows any number of catch: blocks for handling exceptions, along with a final ensure: block. This new idiom mirrors the structure of the Java™ exception handling syntax. This allows Bistro to integrate closely with the exception handling capabilities of Java™. The Bistro compiler recognizes usage of this idiom and translates it directly into the equivalent Java™ code using try { } catch { } finally { }.

Finally, Bistro supports the declaration of exceptions thrown by methods and blocks. Both method and block signatures can include a throws: clause. If a Bistro method signature includes a throws: clause, the compiler translates it directly into the equivalent Java™ method signature - i.e., the Java™ method will also include a throws clause with the same list of exception classes. However, block exceptions require special handling by the compiler.

The signatures of the value methods in the block classes (e.g., ZeroArgumentBlock) do not include exception declarations. For this reason, the Bistro compiler automatically generates wrapper methods for blocks that are declared to throw exceptions. Each generated wrapper method catches any exception thrown by a block and rethrows it as an UnhandledJavaException. Because UnhandledJavaException is derived from RuntimeException, the Java™ compiler will not complain about the exception erasure. Exception erasure is a special kind of type erasure that makes the Java™ exceptions transparent. The following table provides examples of the syntax used to declare method and block exceptions.

Methods with Exceptions Description
unary ; throws: java.lang.Throwable
[ "..." ]
This unary method can throw any exception.
binary: argument ; throws: java.lang.Throwable
[ "..." ]
This binary method can throw any exception.
keyword: a1 : a2 ; throws: IOException
[ "..." ]
This keyword method can throw an IOException.
   
Blocks with Exceptions Description
[ throws: java.lang.Throwable | "..." ] This block can throw any exception. However, if an exception is thrown from within the block, it will be caught and rethrown as an UnhandledJavaException.
[ :argument ; throws: IOException | "..." ] This block can throw an IOException. However, if thrown from within the block, it will be caught and rethrown as an UnhandledJavaException.

Java™ is a trademark of Sun Microsystems, Inc.

Permission is granted to copy this document provided this copyright statement is retained in all copies.
Copyright 1999-2001 Nikolas S. Boyd.