Exceptions

This is a basic overview of how to deal with exceptions from PIR. There are two main topics: throwing exceptions and catching exceptions. We'll start with the first.

Throwing exceptions

If you're going to be using exceptions, you probably want to start by including two pasm files that define constants for exception type and severity.

    .include 'include/except_types.pasm'
    .include 'include/except_severity.pasm'

You create exceptions just like you create any other object, with new.

    .local pmc ex
    ex = new 'Exception'

You usually want to at least set a descriptive message about what went wrong.

    ex = "Everything is horrible, dood."

You also usually want to set a severity and sometimes a type. You can find these in "parrot/include/except_severity.pasm" in runtime.

    ex['severity'] = .EXCEPT_DOOMED
    ex['type'] = .CONTROL_ERROR

You actually throw the exception by using the throw op.

    throw ex

Put all together, it looks like this:

Throw Example

    .include 'include/except_types.pasm'
    .include 'include/except_severity.pasm'
    .local pmc ex
    ex = new 'Exception'
    ex = "Everything is horrible, dood."
    ex['severity'] = .EXCEPT_DOOMED
    ex['type'] = .CONTROL_ERROR
    throw ex

Catching exceptions

Parrot maintains a stack of exception handlers. When an exception is thrown, Parrot iterates through the stack looking for a handler that can handle the exception. When it finds a valid exception handler, the exception handler is invoked with the exception as an argument. Exception handlers run in the context of the throw that they're handling.

You create exception handlers just like you create any other object, with new.

    .local pmc eh
    eh = new 'ExceptionHandler'

You set the target of the exception handler (the code that will be invoked) with the set_attr opcode. Usually this will be a label. You manipulate the exception handler stack with the push_eh and pop_eh opcodes. This is a fairly standard use of exception handlers:

    .local pmc eh
    eh = new 'ExceptionHandler'
    set_addr eh, handler
    push_eh eh
    ... # code that might throw an exception
    pop_eh
    .return (1) # Success!
  handler:
    .local pmc ex
    .get_results (ex)
    ... # code that prints a warning, logs an error, whatever
    .return (0) # Failure!

Sometimes you want to be more specific in what you catch. You can set filters on the exception handler based on exception type and severity. The methods you use include .min_severity(), .max_severity(), .handle_types(), and .handle_types_except().

Here's an example of a sub that catches only error exceptions and prints an appropriate log message before exiting.

    .include 'include/except_severity.pasm'

    .sub 'dostuff'
        .local pmc eh
        eh = new 'ExceptionHandler'
        set_addr eh, handler
        eh.'min_severity'(.EXCEPT_ERROR)
        push_eh eh
        # ... stuff that might throw an error
        pop_eh
        .return (1)
      handler:
        .local pmc ex
        .local string msg
        .get_results (ex)
        print "There was a fatal error in dostuff: "
        msg = ex
        say ex
        exit 1
    .end