# Copyright (C) 2001-2006, The Perl Foundation.
# $Id: pdd23_exceptions.pod 12841 2006-05-30 15:23:20Z coke $

=head1 NAME

docs/pdds/pdd23_exceptions.pod - Parrot Exceptions

=head1 ABSTRACT

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

=head1 VERSION

$Revision: 12841 $

=head1 DESCRIPTION

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 -- I<including the spans
of opcodes that the programmer expects to throw exceptions, and how the
programmer wants to handle them.>

=head2 Exception PIR Directives

These are the PIR directives relevant to exceptions and exception handlers:

=over

=item B<.begin_eh I<LABEL>>

A C<.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 I<LABEL>.

[XXX - Is a label a good approach?  Treating exception handlers as label jumps
rather than full subroutines may be error-prone, but having the lexical stack
conveniently at hand is worth a lot.]

=item B<.end_eh>

A C<.end_eh> marks the end of the most recent (innermost) still-open exception
handler opcode span.

=back

=head2 Exception Opcodes

These are the opcodes relevant to exceptions and exception handlers:

=over

=item B<throw I<PMC>>

The C<throw> opcode throws the given PMC as an exception.

Any PMC can be thrown, as long as you're prepared to catch it.  If there's any
chance of cross-language calls -- and in a Parrot environment, cross-language
operations are kind of the point -- then be prepared to catch object of
classes you would never throw yourself.

However, it is I<VERY STRONGLY RECOMMENDED> for inter-HLL operation that any
thrown PMC that can possibly escape your private sandbox should meet the
minimal interface requirements of the C<parrot;exception> class.

=item B<rethrow>

The C<rethrow> opcode rethrows the exception object which is currently being
handled.  It can only be called from inside an exception handler.

=item B<die> I<-- dead>

The C<die> opcode is, ironically enough, now dead.  This section of the docs
will be deleted soon.

=item B<exit>

The C<exit> opcode throws an exception of type C<parrot;exception;exit>.  If
not caught, this exception results eventually in Parrot executing C<exit(0)>.

=item B<pushaction I<SUBPMC>>

C<pushaction> pushes a subroutine object onto the control stack.  If the
control stack is unwound due to an exception (or C<popmark>, or subroutine
return), the subroutine is invoked with an integer argument: C<0> means a
normal return is in progress; C<1> means the stack is unwinding due to an
exception.

[XXX - Seems like there's lots of room for dangerous collisions here.
Keep on the lookout.]

=back

=head1 STANDARD EXCEPTION CLASSES

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 ]---------------


=head1 HOW PARROT HANDLES EXCEPTIONS

[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.]

=head2 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 C<div>, C<fdiv>, and C<cmod>.

The C<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 C<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 C<find_charset> opcode throws an exception if the charset name it's
looking up doesn't exist. The C<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 C<find_encoding> opcode throws an exception if the encoding name
it's looking up doesn't exist. The C<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 C<LexPad> PMC uses exceptions, though
other implementations can choose to return error values instead.
C<store_lex> throws an exception when asked to store a lexical variable
in a name that doesn't exist. C<find_lex> throws an exception when asked
to retrieve a lexical name that doesn't exist.

Other opcodes respond to an C<errorson> setting to decide whether to
throw an exception or return an error value. C<find_global> throws an
exception (or returns a Null PMC) if the global name requested doesn't
exist. C<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 C<LexPad>s and the C<errorson> settings provide the option
for exception-based or non-exception-based implementations, rather than
forcing one or the other.]

=head2 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 C<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
(C<clear_eh>) pops the exception handler off the control stack and
normal execution continues. If the C<find_global> call doesn't find
C<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
C<rethrow> the exception to pass it along to an exception handler
deeper in the stack. The C<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
C<find_global> above or by the C<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 C<bsr> subroutine calls and exception
handlers are both pushed onto the control stack, it's generally a bad
idea to combine the two.

=head1 ATTACHMENTS

None.

=head1 FOOTNOTES

None.

=head1 REFERENCES

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

=cut

__END__
Local Variables:
  fill-column:78
End:
