/*
Copyright (C) 2001-2008, The Perl Foundation.
$Id: /mirror/trunk/src/pmc/exceptionhandler.pmc 34125 2008-12-19T01:57:06.072455Z chromatic  $

=head1 NAME

src/pmc/exceptionhandler.pmc - Exception Handler PMC

=head1 DESCRIPTION

When an exception handler is called, the exception object is passed as
C<REG_PMC(interp, 5)>, the original contents of this register is in the
exception object's C<_P5> attribute.

=head2 Methods

=over 4

=cut

*/

#include "parrot/parrot.h"
#include "parrot/oplib/ops.h"

pmclass ExceptionHandler extends Continuation need_ext {

    ATTR PMC    *handled_types;
    ATTR PMC    *handled_types_except;
    ATTR INTVAL min_severity;
    ATTR INTVAL max_severity;

/*

=item C<void init()>

Initializes the exception handler.

=cut

*/

    VTABLE void init() {
        Parrot_ExceptionHandler_attributes * const core_struct =
            mem_allocate_zeroed_typed(Parrot_ExceptionHandler_attributes);
        Parrot_cont * const cc     = new_continuation(INTERP, NULL);

        PMC_struct_val(SELF)       = cc;
        cc->invoked                = 0;
        PMC_pmc_val(SELF)          = PMCNULL;
        PMC_data(SELF)             = core_struct;
        core_struct->min_severity  = 0;
        core_struct->max_severity  = 0;
        core_struct->handled_types = PMCNULL;
        core_struct->handled_types_except = PMCNULL;

        /* an exception handler has no separate context; it's only a snapshot
         * of an "earlier" context, which is contained in the interpreter's
         * context - the stacks can only be deeper in the interpreter - so no
         * mark of context is needed */
        PObj_custom_mark_SET(SELF);
        PObj_active_destroy_SET(SELF);
    }
/*

=item C<void mark()>

Mark any active exception handler data as live.

=cut

*/

    VTABLE void mark() {
        Parrot_ExceptionHandler_attributes * const core_struct =
            PARROT_EXCEPTIONHANDLER(SELF);
        if (core_struct->handled_types)
            pobject_lives(interp, (PObj *)core_struct->handled_types);
        if (core_struct->handled_types_except)
            pobject_lives(interp, (PObj *)core_struct->handled_types_except);
    }

    /*
     * can't reuse Continuation's destroy yet -
     * RT #46703 fix ref_count handling for exception_handlers first
     *
     * An ExceptionHandler is kind of a limited Continuation
     * (can only call 'up the stack').  Therefore we probably don't
     * have to convert all RetContinuations to real Continuations
     */

    VTABLE void destroy() {
        Parrot_cont                        * const cc          = PMC_cont(SELF);
        Parrot_ExceptionHandler_attributes * const core_struct =
            PARROT_EXCEPTIONHANDLER(SELF);

        if (cc) {
            mem_sys_free(cc);
            PMC_struct_val(SELF) = NULL;
        }

        if (core_struct) {
            mem_sys_free(core_struct);
            PMC_data(SELF) = NULL;
        }
    }

    VTABLE PMC *clone() {
        PMC * const result = SUPER();
        PObj_custom_mark_CLEAR(result);
        return result;
    }

/*

=item C<void set_integer_native(INTVAL)>

Mark whether the handler has been invoked.

=cut

*/

    VTABLE void set_integer_native(INTVAL value) {
        Parrot_cont * const cc = PMC_cont(SELF);
        cc->invoked = value;
    }

/*

=item C<INTVAL get_integer()>

Check whether the handler has been invoked.

=cut

*/

    VTABLE INTVAL get_integer() {
        Parrot_cont * const cc = PMC_cont(SELF);
        return cc->invoked;
    }

/*

=item C<opcode_t *invoke(void *next)>

Set up the environment for the exception handler to be invoked.

=cut

*/

    VTABLE opcode_t *invoke(void *next) {
        Parrot_cont      *cc           = PMC_cont(SELF);
        Parrot_Context   *from_ctx     = CONTEXT(interp);
        Parrot_Context   *to_ctx       = cc->to_ctx;
        opcode_t         *pc           = cc->address;

        Parrot_continuation_check(interp, SELF, cc);
        Parrot_continuation_rewind_environment(interp, SELF, cc);

        /* switch code segment if needed */
        if (INTERP->code != cc->seg)
            Parrot_switch_to_cs(INTERP, cc->seg, 1);

        return pc;
    }


/*

=back

=head2 Methods

=over 4

=cut

*/

/*

=item C<METHOD can_handle(PMC *exception)>

Report whether the exception handler can handle a particular type of exception.

=cut

*/

    METHOD can_handle(PMC *exception) {
        STRING * const sev    = CONST_STRING(interp, "severity");
        STRING * const ex_str = CONST_STRING(interp, "Exception");

        Parrot_ExceptionHandler_attributes * const core_struct =
                    PARROT_EXCEPTIONHANDLER(SELF);
        INTVAL severity = VTABLE_get_integer_keyed_str(interp, exception, sev);

        if (exception->vtable->base_type == enum_class_Exception
        ||  VTABLE_isa(INTERP, exception, ex_str)) {

            if (severity < core_struct->min_severity) {
                RETURN(INTVAL 0);
            }
            if (core_struct->max_severity > 0
                 &&  severity                  > core_struct->max_severity) {
                RETURN(INTVAL 0);
            }
            if (core_struct->handled_types != PMCNULL) {
                INTVAL elems = VTABLE_elements(interp, core_struct->handled_types);
                INTVAL type  = VTABLE_get_integer_keyed_str(interp, exception, CONST_STRING(interp, "type"));
                INTVAL i;

                for (i = 0; i < elems; i++) {
                    INTVAL handled_type = VTABLE_get_integer_keyed_int(interp,
                            core_struct->handled_types, i);
                    if (handled_type == type)
                        RETURN(INTVAL 1);
                }

                RETURN(INTVAL 0);
            }
            if (core_struct->handled_types_except != PMCNULL) {
                INTVAL elems = VTABLE_elements(interp, core_struct->handled_types_except);
                INTVAL type  = VTABLE_get_integer_keyed_str(interp, exception, CONST_STRING(interp, "type"));
                INTVAL i;

                for (i = 0; i < elems; i++) {
                    INTVAL handled_type = VTABLE_get_integer_keyed_int(interp,
                            core_struct->handled_types_except, i);
                    if (handled_type == type)
                        RETURN(INTVAL 0);
                }

                RETURN(INTVAL 1);
            }
            else if (core_struct->max_severity > 0 ||
                    core_struct->min_severity > 0) {
                RETURN(INTVAL 1);
            }

            RETURN(INTVAL 1);
        }

        RETURN(INTVAL 0);
    }

/*

=item C<METHOD min_severity(INTVAL severity :optional, INTVAL have_severity :opt_flag)>

Set or get the minimum severity of the exception handler.

=cut

*/

    METHOD min_severity(INTVAL severity :optional, INTVAL have_severity :opt_flag) {
        Parrot_ExceptionHandler_attributes * const core_struct =
                    PARROT_EXCEPTIONHANDLER(SELF);

        if (have_severity)
            core_struct->min_severity = severity;
        else
            severity = core_struct->min_severity;

        RETURN(INTVAL severity);
    }

/*

=item C<METHOD max_severity(INTVAL severity :optional, INTVAL have_severity :opt_flag)>

Set or get the maximum severity of the exception handler.

=cut

*/

    METHOD max_severity(INTVAL severity :optional, INTVAL have_severity :opt_flag) {
        Parrot_ExceptionHandler_attributes * const core_struct =
                    PARROT_EXCEPTIONHANDLER(SELF);

        if (have_severity)
            core_struct->max_severity = severity;
        else
            severity = core_struct->max_severity;

        RETURN(INTVAL severity);
    }

/*

=item C<METHOD handle_types(PMC *types :slurpy)>

Set the exception types that the ExceptionHandler will handle.

=cut

*/

    METHOD handle_types(PMC *types :slurpy) {
        Parrot_ExceptionHandler_attributes * const core_struct =
                    PARROT_EXCEPTIONHANDLER(SELF);
        core_struct->handled_types =
            VTABLE_elements(interp, types) > 0
                ? types
                : PMCNULL;
    }

/*

=item C<METHOD handle_types_except(PMC *types :slurpy)>

Set the exception types that the ExceptionHandler will not handle.

=cut

*/

    METHOD handle_types_except(PMC *types :slurpy) {
        Parrot_ExceptionHandler_attributes * const core_struct =
                    PARROT_EXCEPTIONHANDLER(SELF);
        core_struct->handled_types_except =
            VTABLE_elements(interp, types) > 0
                ? types
                : PMCNULL;
    }

}

/*

=back

=cut

*/

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */
