Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Make deferal failures into a kind of soft fail rather than an exception.
  • Loading branch information
jnthn committed Aug 14, 2009
1 parent bae2cfa commit 4245753
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 4 deletions.
29 changes: 27 additions & 2 deletions src/builtins/control.pir
Expand Up @@ -444,6 +444,7 @@ on error.
.local pmc clist, lexpad, self, next
get_next_candidate_info clist, $P0, lexpad
next = clone clist
next.'set_failure_mode'()
$P0 = deref next
$I0 = isa $P0, 'Method'
unless $I0 goto not_method
Expand All @@ -467,14 +468,26 @@ on error.
.local pmc clist, lexpad, self, next, result
get_next_candidate_info clist, $P0, lexpad
next = clone clist
next.'set_failure_mode'()
$P0 = deref next
$I0 = isa $P0, 'Method'
unless $I0 goto not_method
self = lexpad['self']
(result) = next(self, pos_args :flat, named_args :flat :named)
'return'(result)
goto process_result
not_method:
(result) = next(pos_args :flat, named_args :flat :named)

process_result:
$I0 = isa result, ['Failure']
unless $I0 goto did_defer
$P0 = getattribute result, '$!exception'
if null $P0 goto did_defer
$S0 = $P0['message']
if $S0 != 'No method to defer to' goto did_defer
.return (result)

did_defer:
'return'(result)
.end

Expand All @@ -493,6 +506,7 @@ on error.
# and tailcall the next candidate.
.local pmc pos_args, named_args
(pos_args, named_args) = '!get_original_args'(routine, lexpad)
next.'set_failure_mode'()
.tailcall next(pos_args :flat, named_args :flat :named)
.end

Expand All @@ -509,10 +523,21 @@ on error.

# Build arguments based upon what the caller was originall invoked with,
# get the result of the next candidate and use return to retrun from
# the caller.
# the caller, provided the defer did not fail.
.local pmc pos_args, named_args, result
(pos_args, named_args) = '!get_original_args'(routine, lexpad)
next.'set_failure_mode'()
(result) = next(pos_args :flat, named_args :flat :named)

$I0 = isa result, ['Failure']
unless $I0 goto did_defer
$P0 = getattribute result, '$!exception'
if null $P0 goto did_defer
$S0 = $P0['message']
if $S0 != 'No method to defer to' goto did_defer
.return (result)

did_defer:
'return'(result)
.end

Expand Down
15 changes: 15 additions & 0 deletions src/builtins/guts.pir
Expand Up @@ -1535,6 +1535,21 @@ return the resume continuation so we can continue execution after the bind.
.return ($P0)
.end


=item !deferal_fail

Used by P6invocation to help us get soft-failure semantics when no deferal
is possible.

=cut

.sub '!deferal_fail'
.param pmc pos_args :slurpy
.param pmc named_args :slurpy :named
.lex '__CANDIDATE_LIST__', $P0
.tailcall '!FAIL'('No method to defer to')
.end

=back

=cut
Expand Down
29 changes: 27 additions & 2 deletions src/pmc/p6invocation.pmc
Expand Up @@ -22,9 +22,17 @@ gets stuck into the lex pad to represent the the candidate list.
#include "pmc_perl6multisub.h"


/* Selects whether we're doing a real dispatch or just a test to see if
* we could dispatch. */
#define P6I_MODE_DISPATCH 0
#define P6I_MODE_CHECK 1


/* Flag on the PMC to say whether we're in quiet failure mode (if set)
* or exception mode if we are unable to dispatch. */
#define PObj_P6I_FAILURE_MODE_FLAG PObj_private0_FLAG


/* Declaration; definition of this is in p6opaque.pmc. */
PMC *look_for_method(PARROT_INTERP, PMC *search_list, INTVAL *start_pos, STRING *name);

Expand Down Expand Up @@ -165,6 +173,8 @@ pmclass P6Invocation need_ext dynpmc group perl6_group {
SETATTR_P6Invocation_search_list(interp, copy, search_list);
SETATTR_P6Invocation_name(interp, copy, name);
SETATTR_P6Invocation_resume_point(interp, copy, resume_point);
if (PObj_flag_TEST(P6I_FAILURE_MODE, SELF))
PObj_flag_SET(P6I_FAILURE_MODE, copy);
return copy;
}

Expand Down Expand Up @@ -220,8 +230,19 @@ pmclass P6Invocation need_ext dynpmc group perl6_group {
SETATTR_P6Invocation_first_candidate(interp, SELF, PMCNULL);

/* Invoke it, then fudge ourself into its lexpad. */
if (PMC_IS_NULL(first_candidate))
Parrot_ex_throw_from_c_args(interp, next, 1, "No candidates found to invoke");
if (PMC_IS_NULL(first_candidate)) {
PMC *ns;

/* Oh noes, no candidate. If we aren't in soft-fail mode, then
* throw an exception. */
if (!PObj_flag_TEST(P6I_FAILURE_MODE, SELF))
Parrot_ex_throw_from_c_args(interp, next, 1, "No candidates found to invoke");

/* Otherwise, we look up something that when invoked will just give a
* dispatch failure. */
ns = Parrot_get_namespace_keyed_str(interp, interp->root_namespace, CONST_STRING(interp, "perl6"));
first_candidate = VTABLE_get_pmc_keyed_str(interp, ns, CONST_STRING(interp, "!deferal_fail"));
}
addr = VTABLE_invoke(interp, first_candidate, next);
lexpad = CONTEXT(interp)->lex_pad;
if (!PMC_IS_NULL(lexpad) && VTABLE_exists_keyed_str(interp, lexpad, lexname))
Expand Down Expand Up @@ -259,4 +280,8 @@ pmclass P6Invocation need_ext dynpmc group perl6_group {
SETATTR_P6Invocation_search_list(interp, SELF, PMCNULL);
SETATTR_P6Invocation_resume_point(interp, SELF, 0);
}

METHOD void set_failure_mode() {
PObj_flag_SET(P6I_FAILURE_MODE, SELF);
}
}

0 comments on commit 4245753

Please sign in to comment.