The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

docs/pdds/clip/pddXX_exceptions.pod - Parrot Exceptions

ABSTRACT

This document defines the requirements and implementation strategy for Parrot's exception system.

VERSION

$Revision: $

DESCRIPTION

An exception system gives user-developed code control over how run-time error conditions are handled. Exceptions are errors or unusual conditions that requires special processing. An exception handler performs the necessary steps to appropriately respond to a particular kind of exception.

Exception Opcodes

These are the opcodes relevant to exceptions and exception handlers:

  • push_eh creates an exception handler and pushes it onto the control stack. It takes a label (the location of the exception handler) as its only argument. [Is this right? Treating exception handlers as label jumps rather than full subroutines seems error-prone.]

  • clear_eh removes the most recently added exception from the control stack.

  • throw throws an exception object.

  • rethrow rethrows an exception object. It can only be called from inside an exception handler.

  • die throws an exception. It takes two arguments, one for the severity of the exception and one for the type of exception.

    If the severity is EXCEPT_DOOMED, it exits via a call to _exit($2), which is not a catchable exception.

    These are the constants defined for severity:

      0    EXCEPT_NORMAL
      1    EXCEPT_WARNING
      2    EXCEPT_ERROR
      3    EXCEPT_SEVERE
      4    EXCEPT_FATAL
      5    EXCEPT_DOOMED
      6    EXCEPT_EXIT

    These are the constants defined for exception types:

      0    E_Exception
      1    E_SystemExit
      2    E_StopIteration
      3    E_StandardError
      4    E_KeyboardInterrupt
      5    E_ImportError
      6    E_EnvironmentError
      7    E_IOError
      8    E_OSError
      9    E_WindowsError
      10   E_VMSError
      11   E_EOFError
      12   E_RuntimeError
      13   E_NotImplementedError
      14   E_LibraryNotLoadedError
      15   E_NameError
      16   E_UnboundLocalError
      17   E_AttributeError
      18   E_SyntaxError
      19   E_IndentationError
      20   E_TabError
      21   E_TypeError
      22   E_AssertionError
      23   E_LookupError
      24   E_IndexError
      25   E_KeyError
      26   E_ArithmeticError
      27   E_OverflowError
      28   E_ZeroDivisionError
      29   E_FloatingPointError
      30   E_ValueError
      31   E_UnicodeError
      32   E_UnicodeEncodeError
      33   E_UnicodeDecodeError
      34   E_UnicodeTranslateError
      35   E_ReferenceError
      36   E_SystemError
      37   E_MemoryError
      37   E_LAST_PYTHON_E
      38   BAD_BUFFER_SIZE
      39   MISSING_ENCODING_NAME
      40   INVALID_STRING_REPRESENTATION
      41   ICU_ERROR
      42   UNIMPLEMENTED
      43   NULL_REG_ACCESS
      44   NO_REG_FRAMES
      45   SUBSTR_OUT_OF_STRING
      46   ORD_OUT_OF_STRING
      47   MALFORMED_UTF8
      48   MALFORMED_UTF16
      49   MALFORMED_UTF32
      50   INVALID_CHARACTER
      51   INVALID_CHARTYPE
      52   INVALID_ENCODING
      53   INVALID_CHARCLASS
      54   NEG_REPEAT
      55   NEG_SUBSTR
      56   NEG_SLEEP
      57   NEG_CHOP
      58   INVALID_OPERATION
      59   ARG_OP_NOT_HANDLED
      60   KEY_NOT_FOUND
      61   JIT_UNAVAILABLE
      62   EXEC_UNAVAILABLE
      63   INTERP_ERROR
      64   PREDEREF_LOAD_ERROR
      65   PARROT_USAGE_ERROR
      66   PIO_ERROR
      67   PARROT_POINTER_ERROR
      68   DIV_BY_ZERO
      69   PIO_NOT_IMPLEMENTED
      70   ALLOCATION_ERROR
      71   INTERNAL_PANIC
      72   OUT_OF_BOUNDS
      73   JIT_ERROR
      74   EXEC_ERROR
      75   ILL_INHERIT
      76   NO_PREV_CS
      77   NO_CLASS
      78   LEX_NOT_FOUND
      79   PAD_NOT_FOUND
      80   ATTRIB_NOT_FOUND
      81   GLOBAL_NOT_FOUND
      82   METH_NOT_FOUND
      83   WRITE_TO_CONSTCLASS
      84   NOSPAWN
      85   INTERNAL_NOT_IMPLEMENTED
      86   ERR_OVERFLOW
      87   LOSSY_CONVERSION
  • exit throws an exception of severity EXCEPT_EXIT. It takes a single argument for the exception type.

  • pushaction pushes a subroutine object onto the control stack. If the control stack is unwound due to an exception (or popmark, or subroutine return), the subroutine is invoked with an integer argument: 0 means a normal return; 1 means an exception has been raised. [Seems like there's lots of room for dangerous collisions here.]

IMPLEMENTATION

[I'm not convinced the control stack is the right way to handle exceptions. Most of Parrot is based on the continuation-passing style of control, shouldn't exceptions be based on it too?]

Opcodes that Throw Exceptions

Exceptions have been incorporated into built-in opcodes in a limited way, but they aren't used consistently.

Divide by zero exceptions are thrown by div, fdiv, and cmod.

The ord opcode throws an exception when it's passed an empty argument, or passed a string index that's outside the length of the string.

The classoffset opcode throws an exception when it's asked to retrieve the attribute offset for a class that isn't in the object's inheritance hierarchy.

The find_charset opcode throws an exception if the charset name it's looking up doesn't exist. The trans_charset opcode throws an exception on "information loss" (presumably, this means when one charset doesn't have a one-to-one correspondence in the other charset).

The find_encoding opcode throws an exception if the encoding name it's looking up doesn't exist. The trans_encoding opcode throws an exception on "information loss" (presumably, this means when one encoding doesn't have a one-to-one correspondence in the other encoding).

Parrot's default version of the LexPad PMC uses exceptions, though other implementations can choose to return error values instead. store_lex throws an exception when asked to store a lexical variable in a name that doesn't exist. find_lex throws an exception when asked to retrieve a lexical name that doesn't exist.

Other opcodes respond to an errorson setting to decide whether to throw an exception or return an error value. find_global throws an exception (or returns a Null PMC) if the global name requested doesn't exist. find_name throws an exception (or returns a Null PMC) if the name requested doesn't exist in a lexical, current, global, or built-in namespace.

It's a little odd that so few opcodes throw exceptions (these are the ones that are documented, but a few others throw exceptions internally even though they aren't documented as doing so). It's worth considering either expanding the use of exceptions consistently throughout the opcode set, or eliminating exceptions from the opcode set entirely. The strategy for error handling should be consistent, whatever it is. [I like the way LexPads and the errorson settings provide the option for exception-based or non-exception-based implementations, rather than forcing one or the other.]

Excerpt

[Excerpt from "Perl 6 and Parrot Essentials" to seed discussion. Out-of-date in some ways, and in others it was simply speculative.]

Exceptions provide a way of calling a piece of code outside the normal flow of control. They are mainly used for error reporting or cleanup tasks, but sometimes exceptions are just a funny way to branch from one code location to another one.

Exceptions are objects that hold all the information needed to handle the exception: the error message, the severity and type of the error, etc. The class of an exception object indicates the kind of exception it is.

Exception handlers are derived from continuations. They are ordinary subroutines that follow the Parrot calling conventions, but are never explicitly called from within user code. User code pushes an exception handler onto the control stack with the set_eh opcode. The system calls the installed exception handler only when an exception is thrown.

    newsub P20, .Exception_Handler, _handler
    set_eh P20                  # push handler on control stack
    null P10                    # set register to null
    find_global P10, "none"     # may throw exception
    clear_eh                    # pop the handler off the stack
    ...

  _handler:                     # if not, execution continues here
    is_null P10, not_found      # test P10
    ...

This example creates a new exception handler subroutine with the newsub opcode and installs it on the control stack with the set_eh opcode. It sets the P10 register to a null value (so it can be checked later) and attempts to retrieve the global variable named none. If the global variable is found, the next statement (clear_eh) pops the exception handler off the control stack and normal execution continues. If the find_global call doesn't find none it throws an exception by pushing an exception object onto the control stack. When Parrot sees that it has an exception, it pops it off the control stack and calls the exception handler _handler.

The first exception handler in the control stack sees every exception thrown. The handler has to examine the exception object and decide whether it can handle it (or discard it) or whether it should rethrow the exception to pass it along to an exception handler deeper in the stack. The rethrow opcode is only valid in exception handlers. It pushes the exception object back onto the control stack so Parrot knows to search for the next exception handler in the stack. The process continues until some exception handler deals with the exception and returns normally, or until there are no more exception handlers on the control stack. When the system finds no installed exception handlers it defaults to a final action, which normally means it prints an appropriate message and terminates the program.

When the system installs an exception handler, it creates a return continuation with a snapshot of the current interpreter context. If the exception handler just returns (that is, if the exception is cleanly caught) the return continuation restores the control stack back to its state when the exception handler was called, cleaning up the exception handler and any other changes that were made in the process of handling the exception.

Exceptions thrown by standard Parrot opcodes (like the one thrown by find_global above or by the throw opcode) are always resumable, so when the exception handler function returns normally it continues execution at the opcode immediately after the one that threw the exception. Other exceptions at the run-loop level are also generally resumable.

  new P10, Exception            # create new Exception object
  set P10["_message"], "I die"  # set message attribute
  throw P10                     # throw it

Exceptions are designed to work with the Parrot calling conventions. Since the return addresses of bsr subroutine calls and exception handlers are both pushed onto the control stack, it's generally a bad idea to combine the two.

ATTACHMENTS

None.

FOOTNOTES

None.

REFERENCES

  src/ops/core.ops
  src/exceptions.c
  runtime/parrot/include/except_types.pasm
  runtime/parrot/include/except_severity.pasm