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

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 in rescue.png

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