Difference between revisions of "Exceptions as Objects"
(→Exception at rescue clause) |
|||
Line 84: | Line 84: | ||
Related queries in EXCEPTION: | Related queries in EXCEPTION: | ||
+ | <eiffel> | ||
is_ignored: BOOLEAN | is_ignored: BOOLEAN | ||
is_caught: BOOLEAN | is_caught: BOOLEAN | ||
is_ignorable: BOOLEAN | is_ignorable: BOOLEAN | ||
+ | </eiffel> | ||
Related routines in EXCEPTION_MANAGER: | Related routines in EXCEPTION_MANAGER: | ||
+ | <eiffel> | ||
is_caught (a_exception: TYPE [EXCEPTION]): BOOLEAN | is_caught (a_exception: TYPE [EXCEPTION]): BOOLEAN | ||
is_ignored (a_exception: TYPE [EXCEPTION]): BOOLEAN | is_ignored (a_exception: TYPE [EXCEPTION]): BOOLEAN | ||
Line 95: | Line 98: | ||
ignore (a_exception: TYPE [EXCEPTION]) | ignore (a_exception: TYPE [EXCEPTION]) | ||
set_is_ignored (a_exception: TYPE [EXCEPTION]; a_ignored: BOOLEAN) | set_is_ignored (a_exception: TYPE [EXCEPTION]; a_ignored: BOOLEAN) | ||
− | + | </eiffel> | |
== Exception at rescue clause == | == Exception at rescue clause == | ||
See the following diagram: | See the following diagram: | ||
Line 134: | Line 137: | ||
== Class ANY == | == Class ANY == | ||
+ | This is now done in EXCEPTION_MANAGER. | ||
<eiffel> | <eiffel> | ||
class ANY | class ANY | ||
Line 225: | Line 229: | ||
create | create | ||
default_create | default_create | ||
+ | |||
+ | feature -- Access | ||
+ | |||
+ | frozen last_exception: EXCEPTION | ||
+ | -- Last exception | ||
feature -- Status report | feature -- Status report | ||
Line 347: | Line 356: | ||
rescue | rescue | ||
retried := True | retried := True | ||
− | l_exception ?= last_exception -- Check `last_exception' is an object of ROUTINE_FAILURE | + | l_exception ?= (create {EXCEPTION_MANAGER}).last_exception -- Check `last_exception' is an object of ROUTINE_FAILURE |
if l_exception /= Void then | if l_exception /= Void then | ||
print ("True" + "%N") | print ("True" + "%N") |
Revision as of 05:55, 8 October 2007
Contents
Overview
This article describes many aspects of Exceptions as Objects (ECMA-367 8.26) which will be implemented in ISE Eiffel. All exceptions will be raised as objects which can be accessed by `last_exception'. Exceptions as objects are known as type EXCEPTION and its descendants. EXCEPTION_MANAGER is introduced to handle common exception class operations, i.e. catch, and ignore.
This doc is from the original working version: http://docs.google.com/Doc?docid=dckspcv8_9htpfm4&hl=en
Exception class hierarchy
EXCEPTION SYSTEM_EXCEPTION MACHINE_EXCEPTION OPERATING_SYSTEM_EXCEPTION COM_FAILURE OPERATING_SYSTEM_FAILURE OPERATING_SYSTEM_SIGNAL_FAILURE HARDWARE_EXCEPTION FLOATING_POINT_FAILURE EIFFEL_EXCEPTION LANGUAGE_EXCEPTION BAD_INSPECT_VALUE VOID_TARGET VOID_ASSIGNED_TO_EXPANDED ROUTINE_FAILURE EIFFELSTUDIO_SPECIFIC_LANGUAGE_EXCEPTION CREATE_ON_DEFERRED ADDRESS_APPLIED_TO_MELTED_FEATURE EIFFEL_RUNTIME_EXCEPTION NO_MORE_MEMORY DATA_EXCEPTION IO_FAILURE SERIALIZATION_FAILURE MISMATCH_FAILURE EXTERNAL_FAILURE-- Only used for threads at the moment EIFFEL_RUNTIME_PANIC ASSERTION_VIOLATION INVARIANT_VIOLATION PRECONDITION_VIOLATION POSTCONDITION_VIOLATION CHECK_VIOLATION VARIANT_VIOLATION OLD_VIOLATION LOOP_INVARIANT_VIOLATION DEVELOPER_EXCEPTION OBSOLETE_EXCEPTION RESUMPTION_FAILURE Was RESUMPTION_FAILED. RESCUE_FAILURE --------- SHOULD NOT APPLY ANY MORE WITH ISO/ECMA, CHECK!!! EXCEPTION_IN_SIGNAL_HANDLER_FAILURE
Classes in red plus EXCEPTION_MANAGER will be placed at base\elks\kernel\exceptions\. The rests are ise specific.
Terminology We use _EXCEPTION for intermediate nodes in the hierarchy; when we need a term in the leaves of the hierarchy we don't use EXCEPTION but
* _VIOLATION for assertions (also used for ASSERTION_VIOLATION, an intermediate node) * _FAILURE otherwise This is consistent with Eiffel exception terminology since from the viewpoint of Eiffel these things have failed, for example a signal was not handled properly. The Eiffel runtime is causing an exception in the Eiffel code as a result, but the original event was a failure. Hence SERIALIZATION_FAILURE etc.
Raise and catch an exception
Catching an exception
All exceptions can possibly be caught. `is_caught' is set by default. Only the given type of exception is `ignore'd in EXCEPTION_MANAGER if possible, it hence is not caught. The caught exception can be accessed by `last_exception'.
Raising an exception
There are two types of exceptions concerning how an exception is raised.
- System raised
Exception raised through the runtime. Most exceptions of this type are predefine as classes in base library. i.e. assertion violations, no memory exceptions and routine failures.
- User raised
User raised exceptions are initialized by users and raised by explicit call of {EXCEPTION}.raise.
Theoretically the runtime can raise any exceptions known. A user can only raise an EXCEPTION that is `is_raisable'. `is_raisable' is possible true only when the exception is of DEVELOPER_EXCEPTION. This means only raising a DEVELOPER_EXCEPTION or its descendant is guaranteed correct.
Ignoring, continuing an exception
Quote from ECMA 8.26.12:
It is possible, through routines of the Kernel Library class EXCEPTION, to ensure that exceptions of certain types be: * Ignored: lead to no change of non-exception semantics. * Continued: lead to execution of a programmer-specified routine, then to continuation of the execution according to non-exception semantics.
An ignorable exception can be ignored. When an exception is ignored, the raising does nothing as if non-exception semantics. What are the exceptions not ignorable?
Related queries in EXCEPTION:
is_ignored: BOOLEAN is_caught: BOOLEAN is_ignorable: BOOLEAN
Related routines in EXCEPTION_MANAGER:
is_caught (a_exception: TYPE [EXCEPTION]): BOOLEAN is_ignored (a_exception: TYPE [EXCEPTION]): BOOLEAN is_ignorable (a_exception: TYPE [EXCEPTION]): BOOLEAN catch (a_exception: TYPE [EXCEPTION]) ignore (a_exception: TYPE [EXCEPTION]) set_is_ignored (a_exception: TYPE [EXCEPTION]; a_ignored: BOOLEAN)
Exception at rescue clause
See the following diagram:
Exception e2 is thrown away at c.f3, instruction of rescue clause, in which e3 is raised. In this case we only keep e3 in the stack and accessible by `last_exception'.
An behavior should be clarified, that when an exception occurs deep in execution level of a rescue clause, `last_exception' is understood as "last unhandled exception".
1. A new exception causes "last_exception" to be saved somewhere before
entering "rescue" clause. After that the new exception object is attached to
"last_exception".
2. If an exception is not handled (there is no rescue clause or "retry" is not performed), a new exception object "ROUTINE_FAILURE" is created, its attribute "original_exception" is set to "last_exception", and "last_exception" is set to this new "ROUTINE_FAILURE" object.
3. If an exception is handled ("retry" is executed), "last_exception" is attached a value saved in step 1.
Example: If "last_exception" in the following code snippet is modified by the call to feature "h" that raises an exception internally and handles it?
rescue -- "last_exception" is set to "X". -- Call feature that raises exception "Y", but handles it. h -- Is "last_exception" set to "X" or to "Y"? -- The answer is X.
Class ANY
This is now done in EXCEPTION_MANAGER.
class ANY ... feature -- Exception frozen last_exception: EXCEPTION -- Last exception external "built_in" end ...
Class EXCEPTION
deferred class interface EXCEPTION feature -- Access code: INTEGER_32 -- Code of the exception. -- |Maybe we don't need this anymore data: ANY -- Data set by user exception_trace: STRING_8 -- String representation of current exception trace meaning: STRING_8 -- A message in English describing what `except' is message: STRING_8 -- Tag of current exception original: EXCEPTION -- Original exception that triggered current exception recipient_name: STRING_8 -- Name of the routine whose execution was -- interrupted by current exception type_name: STRING_8 -- Name of the class that includes the recipient -- of original form of current exception feature -- Status report is_caught: BOOLEAN -- If set, exception is raised. ensure not_is_caught_implies_is_ignorable: not Result implies is_ignorable not_is_ignored: not is_ignored is_ignorable: BOOLEAN -- Is current exception ignorable? is_ignored: BOOLEAN -- If set, no exception is raised. ensure is_ignored_implies_is_ignorable: Result implies is_ignorable not_is_caught: not is_caught is_raisable: BOOLEAN -- Is current exception raisable by raise? feature -- Raise raise -- Raise current exception require is_raisable: is_raisable feature -- Status settings set_data (a_data: like data) -- Set data with `a_data'. ensure data_is_set: data = a_data end -- class EXCEPTION
Class EXCEPTION_MANAGER
class interface EXCEPTION_MANAGER create default_create feature -- Access frozen last_exception: EXCEPTION -- Last exception feature -- Status report is_caught (a_exception: TYPE [EXCEPTION]): BOOLEAN -- If set, type of `a_exception' is raised. ensure not_is_ignored: not is_ignored (a_exception) is_ignorable (a_exception: TYPE [EXCEPTION]): BOOLEAN -- If set, type of `a_exception' is ignorable. is_ignored (a_exception: TYPE [EXCEPTION]): BOOLEAN -- If set, type of `a_exception' is not raised. ensure not_is_caught: not is_caught (a_exception) feature -- Status setting catch (a_exception: TYPE [EXCEPTION]) -- Set type of `a_exception' is_ignored. require a_exception_not_void: a_exception /= Void ensure is_ignored: not is_ignored (a_exception) ignore (a_exception: TYPE [EXCEPTION]) -- Make sure that any exception of code `code' will be -- ignored. This is not the default. require a_exception_not_void: a_exception /= Void is_ignorable: is_ignorable (a_exception) ensure is_caught: is_ignored (a_exception) set_is_ignored (a_exception: TYPE [EXCEPTION]; a_ignored: BOOLEAN) -- Set type of `a_exception' to be `a_ignored'. require a_exception_not_void: a_exception /= Void a_ignored_implies_is_ignorable: a_ignored implies is_ignorable (a_exception) ensure is_ignored_set: is_ignored (a_exception) = a_ignored end -- class EXCEPTION_MANAGER
Default rescue
As specified in ECMA, `default_rescue' in ANY or its descendant is called as unqualified when an internal routine or an attributes with no rescue clause fails. We choose not to invoke it when it is not redefined, or this will be too expensive.
class ANY ... default_rescue is do end ... end
Sample code: Raising and catching developer exception
class MY_EXCEPTION inherit DEVELOPER_EXCEPTION end
class A feature f is local retried: BOOLEAN do if not retried then my_exception.raise -- Raise user-defined exception. end rescue if last_exception = my_exception then -- Check the exception object was the one user defined. print ("True" + "%N") else print ("False" + "%N") end end my_exception: MY_EXCEPTION is once create Result end end
class APPLICATION inherit A create make feature -- Initialization make is -- Run application. local a: A l_exception: ROUTINE_FAILURE retried: BOOLEAN do if not retried then create a a.f end rescue retried := True l_exception ?= (create {EXCEPTION_MANAGER}).last_exception -- Check `last_exception' is an object of ROUTINE_FAILURE if l_exception /= Void then print ("True" + "%N") else print ("False" + "%N") end if last_exception.original = my_exception then -- `original' exception is the one caused `last_exception' print ("True" + "%N") else print ("False" + "%N") end retry end end -- class APPLICATION