Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
implement label handling for rakudo's 'for' loops (nqp::handle)
  • Loading branch information
FROGGS committed May 11, 2014
1 parent 748ef15 commit 51b5ab0
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 40 deletions.
85 changes: 58 additions & 27 deletions src/vm/jvm/QAST/Compiler.nqp
Expand Up @@ -1630,28 +1630,56 @@ QAST::OperationsJAST.add_core_op('handle', :!inlinable, sub ($qastcomp, $op) {
QAST::Op.new( :op('exception') )
)));
my $push_target := $hblock;
my $has_label := 0;
for @children -> $type, $handler {
# Get the category mask.
unless nqp::existskey(%handler_names, $type) {
nqp::die("Invalid handler type '$type'");
if $type eq 'LABELED' {
$has_label := 1;
# Rethrow if a label was requested for which we are not in charge for.
$hblock.push(
QAST::Op.new(
:op('if'),
QAST::Op.new(
:op('bitand_i'),
QAST::Var.new( :name('__category__'), :scope('local') ),
QAST::IVal.new( :value($EX_CAT_LABELED) )
),
QAST::Op.new(
:op('unless'),
QAST::Op.new(
:op('iseq_i'),
QAST::Op.new( :op('where'),
QAST::Op.new( :op('getpayload'), QAST::Op.new( :op('exception') ) )
),
QAST::Op.new( :op('where'), $handler )
),
QAST::Op.new( :op('rethrow'), QAST::Op.new( :op('exception') ) )
)
)
);
}
else {
# Get the category mask.
unless nqp::existskey(%handler_names, $type) {
nqp::die("Invalid handler type '$type'");
}
my $cat_mask := %handler_names{$type};

# Chain in this handler.
my $check := QAST::Op.new(
:op('if'),
QAST::Op.new(
:op('bitand_i'),
QAST::Var.new( :name('__category__'), :scope('local') ),
QAST::IVal.new( :value($cat_mask) )
),
$handler
);
$push_target.push($check);
$push_target := $check;

# Add to mask.
$mask := nqp::bitor_i($mask, $cat_mask);
}
my $cat_mask := %handler_names{$type};

# Chain in this handler.
my $check := QAST::Op.new(
:op('if'),
QAST::Op.new(
:op('bitand_i'),
QAST::Var.new( :name('__category__'), :scope('local') ),
QAST::IVal.new( :value($cat_mask) )
),
$handler
);
$push_target.push($check);
$push_target := $check;

# Add to mask.
$mask := nqp::bitor_i($mask, $cat_mask);
}

# Compile, create a lexical to put the handler in, and add it. Should
Expand Down Expand Up @@ -1685,7 +1713,7 @@ QAST::OperationsJAST.add_core_op('handle', :!inlinable, sub ($qastcomp, $op) {
# after unwind" flag, used to force this whole block to exit.
my $catchil := JAST::InstructionList.new();
my $exitlbl := JAST::Label.new( :name($qastcomp.unique('unwindexit')) );
$qastcomp.unwind_check($catchil, $handler);
$qastcomp.unwind_check($catchil, $handler, :handler_cares($has_label));
$catchil.append(JAST::Instruction.new( :op('getfield'), $TYPE_EX_UNWIND, 'result', $TYPE_SMO ));
$catchil.append(JAST::Instruction.new( :op('astore'), $result ));
$catchil.append(JAST::Instruction.new( :op('aload'), 'cf' ));
Expand Down Expand Up @@ -4376,7 +4404,7 @@ class QAST::CompilerJAST {
# rethrow of the handler. Assumes the exception is on the stack top,
# and that we will not swallow it.
my $unwind_lbl := 0;
method unwind_check($il, $desired, :$label, :$outer = 0) {
method unwind_check($il, $desired, :$label, :$outer = 0, :$handler_cares) {
my $lbl_i := JAST::Label.new( :name('unwind_' ~ $unwind_lbl++) );
my $lbl_c := JAST::Label.new( :name('unwind_' ~ $unwind_lbl++) );
$il.append($DUP);
Expand All @@ -4393,11 +4421,14 @@ class QAST::CompilerJAST {
$il.append($ATHROW);
$il.append($lbl_c);

$il.append($DUP);
$il.append(JAST::PushIVal.new( :value($label ?? nqp::where($label.value) !! 0) ));
$il.append(JAST::PushIVal.new( :value($outer) ));
$il.append(JAST::Instruction.new( :op('aload'), 'tc' ));
$il.append(JAST::Instruction.new( :op('invokestatic'), $TYPE_OPS, '_is_same_label', 'Void', $TYPE_EX_UNWIND, 'Long', 'Long', $TYPE_TC ));
unless $handler_cares {
$il.append($DUP);
$il.append(JAST::PushIVal.new( :value($label ?? nqp::where($label.value) !! 0) ));
$il.append(JAST::PushIVal.new( :value($outer) ));
$il.append(JAST::Instruction.new( :op('aload'), 'tc' ));
$il.append(JAST::Instruction.new( :op('invokestatic'), $TYPE_OPS, '_is_same_label',
'Void', $TYPE_EX_UNWIND, 'Long', 'Long', $TYPE_TC ));
}
}

# Wraps a handler with code to set/clear the current handler.
Expand Down
Expand Up @@ -145,7 +145,7 @@ private static void invokeHandler(ThreadContext tc, long[] handlerInfo,
tc.unwinder.unwindCompUnit = handlerFrame.codeRef.staticInfo.compUnit;
tc.unwinder.category = category;
if (exObj != null)
tc.unwinder.result = (SixModelObject)exObj;
tc.unwinder.payload = (SixModelObject)exObj.payload;
throw tc.unwinder;
case EX_BLOCK:
try {
Expand Down Expand Up @@ -178,6 +178,8 @@ private static void invokeHandler(ThreadContext tc, long[] handlerInfo,
tc.unwinder.unwindTarget = handlerInfo[0];
tc.unwinder.unwindCompUnit = handlerFrame.codeRef.staticInfo.compUnit;
tc.unwinder.result = Ops.result_o(tc.curFrame);
if (exObj != null)
tc.unwinder.payload = (SixModelObject)exObj.payload;
throw tc.unwinder;
default:
throw ExceptionHandling.dieInternal(tc, "Unknown exception kind");
Expand Down
20 changes: 8 additions & 12 deletions src/vm/jvm/runtime/org/perl6/nqp/runtime/Ops.java
Expand Up @@ -4533,19 +4533,15 @@ public static void _is_same_label(UnwindException uwex, long where, long outerHa
return;

if (uwex instanceof UnwindException) {
VMExceptionInstance vmex = (VMExceptionInstance)uwex.result;
if (vmex == null || (vmex.category & ExceptionHandling.EX_CAT_LABELED) == 0)
SixModelObject payload = uwex.payload;
if (payload.hashCode() == where)
return;

if (vmex instanceof VMExceptionInstance) {
if (vmex.payload.hashCode() == where)
return;
/* We're moving to the outside so we do not rethrow to us. */
tc.curFrame.curHandler = outerHandler;
vmex.origin = tc.curFrame;
vmex.nativeTrace = (new Throwable()).getStackTrace();
ExceptionHandling.handlerDynamic(tc, vmex.category, false, vmex);
}
/* We're moving to the outside so we do not rethrow to us. */
VMExceptionInstance vmex = (VMExceptionInstance)newexception(tc);
vmex.category = uwex.category;
vmex.payload = payload;
tc.curFrame.curHandler = outerHandler;
ExceptionHandling.handlerDynamic(tc, vmex.category, false, vmex);
}
else {
throw ExceptionHandling.dieInternal(tc, "_is_same_label needs an object with UnwindException representation");
Expand Down
4 changes: 4 additions & 0 deletions src/vm/jvm/runtime/org/perl6/nqp/runtime/UnwindException.java
Expand Up @@ -18,4 +18,8 @@ public class UnwindException extends ControlException {
* produced.
*/
public SixModelObject result;

/* When we transform a VMException into an UnwindException, we need
* to keep the payload, for example when it is about a loop label. */
public SixModelObject payload;
}

0 comments on commit 51b5ab0

Please sign in to comment.