parrotcode: Parrot Exceptions | |
Contents | Documentation |
docs/pdds/pdd23_exceptions.pod - Parrot Exceptions
This document defines the requirements and implementation strategy for Parrot's exception system.
$Revision$
An exception system gives user-developed code control over how run-time error conditions are handled. Exceptions are errors or unusual conditions that require special processing. An exception handler performs the necessary steps to appropriately respond to a particular kind of exception.
Parrot is designed to support dynamic languages, but Parrot compromises the principle of dynamic behavior when necessary. For example, Parrot requires any given subroutine to be fully compiled before it can be called.
Since the structure and content of a compiled subroutine are fixed at compile time, it would be wasteful use the dynamic execution of opcodes at runtime to keep track of meta-information about that structure -- including the spans of opcodes that the programmer expects to throw exceptions, and how the programmer wants to handle them.
These are the PIR directives relevant to exceptions and exception handlers:
.begin_eh
directive marks the beginning of a span of opcodes which the programmer expects to throw an exception.
If an exception occurs in the execution of the given opcode span,
Parrot will transfer control to LABEL..end_eh
marks the end of the most recent (innermost) still-open exception handler opcode span.These are the opcodes relevant to exceptions and exception handlers:
throw
opcode throws the given PMC as an exception.parrot;exception
class.rethrow
opcode rethrows the exception object which is currently being handled.
It can only be called from inside an exception handler.die
opcode is,
ironically enough,
now dead.
This section of the docs will be deleted soon.exit
opcode throws an exception of type parrot;exception;exit
.
If not caught,
this exception results eventually in Parrot executing exit(0)
.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 is in progress; 1
means the stack is unwinding due to an exception.Parrot comes with a small hierarchy of classes designed to be thrown. Parrot throws them when internal Parrot errors occur, and HLL creators and end users can throw them too.
[[[[ TODO - introduce herarchy and minimal interface ]]]]
-------------------[ WHERE CHIP LEFT OFF EDITING ]--------------------- -------------[ TEXT BELOW THIS POINT IS PROBABLY WRONG ]---------------
[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? See bug #38850.]
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 LexPad
s and the errorson
settings provide the option for exception-based or non-exception-based implementations,
rather than forcing one or the other.]
[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 push_eh
opcode.
The system calls the installed exception handler only when an exception is thrown.
push_eh _handler # push handler on control stack
find_global P10, "none" # may throw exception
clear_eh # pop the handler off the stack
...
_handler: # if not, execution continues here
get_results '(0,0)', P0, S0 # handler is called with (exception, message)
...
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 passing an exception object to the exception 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.
None.
None.
src/ops/core.ops
src/exceptions.c
runtime/parrot/include/except_types.pasm
runtime/parrot/include/except_severity.pasm
|