@@ -1040,8 +1040,10 @@ for ('', 'repeat_') -> $repness {
1040
1040
my $ handler := 1 ;
1041
1041
my @ operands ;
1042
1042
my $ orig_type ;
1043
+ my $ label ;
1043
1044
for $ op . list {
1044
1045
if $ _ . named eq ' nohandler' { $ handler := 0 ; }
1046
+ elsif $ _ . named eq ' label' { $ label := $ _ ; }
1045
1047
else { @ operands . push ($ _ ) }
1046
1048
}
1047
1049
if + @ operands != 2 && + @ operands != 3 {
@@ -1064,8 +1066,8 @@ for ('', 'repeat_') -> $repness {
1064
1066
my $ l_handler_id ;
1065
1067
my $ nr_handler_id ;
1066
1068
if $ handler {
1067
- $ l_handler_id := & * REGISTER_UNWIND_HANDLER ($ * HANDLER_IDX , $ EX_CAT_LAST );
1068
- $ nr_handler_id := & * REGISTER_UNWIND_HANDLER ($ l_handler_id , $ EX_CAT_NEXT +| $ EX_CAT_REDO );
1069
+ $ l_handler_id := & * REGISTER_UNWIND_HANDLER ($ * HANDLER_IDX , $ EX_CAT_LAST , : ex_obj( 1 ) );
1070
+ $ nr_handler_id := & * REGISTER_UNWIND_HANDLER ($ l_handler_id , $ EX_CAT_NEXT +| $ EX_CAT_REDO , : ex_obj( 1 ) );
1069
1071
}
1070
1072
1071
1073
# Emit loop prelude, evaluating condition.
@@ -1128,7 +1130,7 @@ for ('', 'repeat_') -> $repness {
1128
1130
# Add redo and next handler if needed.
1129
1131
if $ handler {
1130
1132
my $ catch := JAST::InstructionList. new ();
1131
- $ qastcomp . unwind_check($ catch , $ nr_handler_id );
1133
+ $ qastcomp . unwind_check($ catch , $ nr_handler_id , : $ label , : outer( $ l_handler_id ) );
1132
1134
$ catch . append (JAST::Instruction. new ( : op(' getfield' ), $ TYPE_EX_UNWIND , ' category' , ' Long' ));
1133
1135
$ catch . append (JAST::PushIVal. new ( : value($ EX_CAT_REDO ) ));
1134
1136
$ catch . append ($ LCMP );
@@ -1155,7 +1157,7 @@ for ('', 'repeat_') -> $repness {
1155
1157
# If needed, wrap the whole thing in a last exception handler.
1156
1158
if $ handler {
1157
1159
my $ catch := JAST::InstructionList. new ();
1158
- $ qastcomp . unwind_check($ catch , $ l_handler_id );
1160
+ $ qastcomp . unwind_check($ catch , $ l_handler_id , : $ label , : outer( $ * HANDLER_IDX ) );
1159
1161
$ catch . append ($ POP );
1160
1162
$ il := $ qastcomp . delimit_handler(
1161
1163
JAST::TryCatch. new ( : try($ il ), : catch($ catch ), : type($ TYPE_EX_UNWIND ) ),
@@ -1179,8 +1181,10 @@ for ('', 'repeat_') -> $repness {
1179
1181
QAST ::OperationsJAST. add_core_op(' for' , -> $ qastcomp , $ op {
1180
1182
my $ handler := 1 ;
1181
1183
my @ operands ;
1184
+ my $ label ;
1182
1185
for $ op . list {
1183
1186
if $ _ . named eq ' nohandler' { $ handler := 0 ; }
1187
+ elsif $ _ . named eq ' label' { $ label := $ _ ; }
1184
1188
else { @ operands . push ($ _ ) }
1185
1189
}
1186
1190
@@ -1205,9 +1209,9 @@ QAST::OperationsJAST.add_core_op('for', -> $qastcomp, $op {
1205
1209
my $ n_handler_id ;
1206
1210
my $ r_handler_id ;
1207
1211
if $ handler {
1208
- $ l_handler_id := & * REGISTER_UNWIND_HANDLER ($ * HANDLER_IDX , $ EX_CAT_LAST );
1209
- $ n_handler_id := & * REGISTER_UNWIND_HANDLER ($ l_handler_id , $ EX_CAT_NEXT );
1210
- $ r_handler_id := & * REGISTER_UNWIND_HANDLER ($ n_handler_id , $ EX_CAT_REDO );
1212
+ $ l_handler_id := & * REGISTER_UNWIND_HANDLER ($ * HANDLER_IDX , $ EX_CAT_LAST , : ex_obj( 1 ) );
1213
+ $ n_handler_id := & * REGISTER_UNWIND_HANDLER ($ l_handler_id , $ EX_CAT_NEXT , : ex_obj( 1 ) );
1214
+ $ r_handler_id := & * REGISTER_UNWIND_HANDLER ($ n_handler_id , $ EX_CAT_REDO , : ex_obj( 1 ) );
1211
1215
}
1212
1216
1213
1217
# Evaluate the thing we'll iterate over, get the iterator and
@@ -1285,7 +1289,7 @@ QAST::OperationsJAST.add_core_op('for', -> $qastcomp, $op {
1285
1289
# Wrap block invocation in redo handler if needed.
1286
1290
if $ handler {
1287
1291
my $ catch := JAST::InstructionList. new ();
1288
- $ qastcomp . unwind_check($ catch , $ r_handler_id );
1292
+ $ qastcomp . unwind_check($ catch , $ r_handler_id , : $ label , : outer( $ n_handler_id ) );
1289
1293
$ catch . append ($ POP );
1290
1294
$ catch . append (JAST::Instruction. new ( : op(' goto' ), $ lbl_redo ));
1291
1295
$ inv_il := $ qastcomp . delimit_handler(
@@ -1297,7 +1301,7 @@ QAST::OperationsJAST.add_core_op('for', -> $qastcomp, $op {
1297
1301
# Wrap value fetching and call in "next" handler if needed.
1298
1302
if $ handler {
1299
1303
my $ catch := JAST::InstructionList. new ();
1300
- $ qastcomp . unwind_check($ catch , $ n_handler_id );
1304
+ $ qastcomp . unwind_check($ catch , $ n_handler_id , : $ label , : outer( $ l_handler_id ) );
1301
1305
$ catch . append ($ POP );
1302
1306
$ val_il := $ qastcomp . delimit_handler(
1303
1307
JAST::TryCatch. new ( : try($ val_il ), : $ catch , : type($ TYPE_EX_UNWIND ) ),
@@ -1309,7 +1313,7 @@ QAST::OperationsJAST.add_core_op('for', -> $qastcomp, $op {
1309
1313
# Emit postlude, wrapping in last handler if needed.
1310
1314
if $ handler {
1311
1315
my $ catch := JAST::InstructionList. new ();
1312
- $ qastcomp . unwind_check($ catch , $ l_handler_id );
1316
+ $ qastcomp . unwind_check($ catch , $ l_handler_id , : $ label , : outer( $ * HANDLER_IDX ) );
1313
1317
$ catch . append ($ POP );
1314
1318
$ catch . append (JAST::Instruction. new ( : op(' goto' ), $ lbl_done ));
1315
1319
$ loop_il := $ qastcomp . delimit_handler(
@@ -1711,15 +1715,54 @@ my %control_map := nqp::hash(
1711
1715
' redo' , $ EX_CAT_REDO
1712
1716
);
1713
1717
QAST ::OperationsJAST. add_core_op(' control' , -> $ qastcomp , $ op {
1718
+ my $ label ;
1719
+ for $ op . list {
1720
+ if $ _ . named eq ' label' { $ label := $ _ ; }
1721
+ }
1714
1722
my $ name := $ op . name ;
1715
1723
if nqp ::existskey(% control_map , $ name ) {
1716
1724
my $ cat := % control_map {$ name };
1717
1725
my $ il := JAST::InstructionList. new ();
1718
1726
$ * STACK . spill_to_locals($ il );
1719
- $ il . append (JAST::PushIVal. new ( : value($ cat ) ));
1720
- $ il . append ($ ALOAD_1 );
1721
- $ il . append (savesite(JAST::Instruction. new ( : op(' invokestatic' ), $ TYPE_OPS ,
1722
- ' throwcatdyn_c' , ' Void' , ' Long' , $ TYPE_TC )));
1727
+ if $ label {
1728
+ my $ new_ex := $ * TA . fresh_o();
1729
+
1730
+ # Create a new exception object
1731
+ $ il . append ($ ALOAD_1 ); # TC
1732
+ $ il . append (JAST::Instruction. new ( : op(' invokestatic' ), $ TYPE_OPS ,
1733
+ ' newexception' , $ TYPE_SMO , $ TYPE_TC ));
1734
+ $ il . append (JAST::Instruction. new ( : op(' astore' ), $ new_ex ));
1735
+
1736
+ # Store the label as payload
1737
+ $ il . append (JAST::Instruction. new ( : op(' aload' ), $ new_ex ));
1738
+ my $ payload := $ qastcomp . as_jast($ label , : want($ RT_OBJ ));
1739
+ $ il . append ($ payload . jast);
1740
+ $ * STACK . obtain($ il , $ payload );
1741
+ $ il . append (JAST::Instruction. new ( : op(' aload' ), ' tc' ));
1742
+ $ il . append (JAST::Instruction. new ( : op(' invokestatic' ), $ TYPE_OPS ,
1743
+ ' setpayload' , $ TYPE_SMO , $ TYPE_SMO , $ TYPE_SMO , $ TYPE_TC ));
1744
+ $ il . append ($ POP ); # discard payload
1745
+
1746
+ # Set exception type
1747
+ $ il . append (JAST::Instruction. new ( : op(' aload' ), $ new_ex ));
1748
+ $ il . append (JAST::PushIVal. new ( : value($ cat ) ));
1749
+ $ il . append (JAST::Instruction. new ( : op(' aload' ), ' tc' ));
1750
+ $ il . append (JAST::Instruction. new ( : op(' invokestatic' ), $ TYPE_OPS ,
1751
+ ' setextype' , ' Long' , $ TYPE_SMO , ' Long' , $ TYPE_TC ));
1752
+ $ il . append ($ POP2 ); # discard exception category
1753
+
1754
+ # Throw it
1755
+ $ il . append (JAST::Instruction. new ( : op(' aload' ), $ new_ex ));
1756
+ $ il . append (JAST::Instruction. new ( : op(' aload' ), ' tc' ));
1757
+ $ il . append (savesite(JAST::Instruction. new ( : op(' invokestatic' ), $ TYPE_OPS ,
1758
+ ' _throw_c' , ' Void' , $ TYPE_SMO , $ TYPE_TC )));
1759
+ }
1760
+ else {
1761
+ $ il . append (JAST::PushIVal. new ( : value($ cat ) ));
1762
+ $ il . append ($ ALOAD_1 );
1763
+ $ il . append (savesite(JAST::Instruction. new ( : op(' invokestatic' ), $ TYPE_OPS ,
1764
+ ' throwcatdyn_c' , ' Void' , ' Long' , $ TYPE_TC )));
1765
+ }
1723
1766
result_from_cf($ il , $ RT_OBJ );
1724
1767
}
1725
1768
else {
@@ -4325,7 +4368,7 @@ class QAST::CompilerJAST {
4325
4368
# rethrow of the handler. Assumes the exception is on the stack top,
4326
4369
# and that we will not swallow it.
4327
4370
my $ unwind_lbl := 0 ;
4328
- method unwind_check ($ il , $ desired ) {
4371
+ method unwind_check ($ il , $ desired , : $ label , : $ outer = 0 ) {
4329
4372
my $ lbl_i := JAST::Label. new ( : name(' unwind_' ~ $ unwind_lbl ++ ) );
4330
4373
my $ lbl_c := JAST::Label. new ( : name(' unwind_' ~ $ unwind_lbl ++ ) );
4331
4374
$ il . append ($ DUP );
@@ -4341,6 +4384,12 @@ class QAST::CompilerJAST {
4341
4384
$ il . append (JAST::Instruction. new ( : op(' if_acmpeq' ), $ lbl_c ));
4342
4385
$ il . append ($ ATHROW );
4343
4386
$ il . append ($ lbl_c );
4387
+
4388
+ $ il . append ($ DUP );
4389
+ $ il . append (JAST::PushIVal. new ( : value($ label ?? nqp ::where($ label . value ) !! 0 ) ));
4390
+ $ il . append (JAST::PushIVal. new ( : value($ outer ) ));
4391
+ $ il . append (JAST::Instruction. new ( : op(' aload' ), ' tc' ));
4392
+ $ il . append (JAST::Instruction. new ( : op(' invokestatic' ), $ TYPE_OPS , ' _is_same_label' , ' Void' , $ TYPE_EX_UNWIND , ' Long' , ' Long' , $ TYPE_TC ));
4344
4393
}
4345
4394
4346
4395
# Wraps a handler with code to set/clear the current handler.
0 commit comments