tagged release 0.6.4
[parrot.git] / t / op / exceptions.t
blobf5e07aba310b71cbc0b8e54b78c2c2d7007fe023
1 #! perl
2 # Copyright (C) 2001-2007, The Perl Foundation.
3 # $Id$
5 use strict;
6 use warnings;
7 use lib qw( . lib ../lib ../../lib );
8 use Test::More;
9 use Parrot::Test tests => 33;
11 =head1 NAME
13 t/pmc/exception.t - Exception Handling
15 =head1 SYNOPSIS
17     % prove t/pmc/exception.t
19 =head1 DESCRIPTION
21 Tests C<Exception> and C<Exception_Handler> PMCs.
23 =cut
25 pasm_output_is( <<'CODE', <<'OUTPUT', "push_eh label - pop_eh" );
26     push_eh _handler
27     print "ok 1\n"
28     pop_eh
29     print "ok 2\n"
30     end
31 _handler:
32     end
33 CODE
34 ok 1
35 ok 2
36 OUTPUT
38 pasm_output_is( <<'CODE', <<'OUTPUT', "push_eh eh - pop_eh" );
39     new P29, 'Exception_Handler'
40     push_eh P29
41     print "ok 1\n"
42     pop_eh
43     print "ok 2\n"
44     end
45 CODE
46 ok 1
47 ok 2
48 OUTPUT
50 pasm_output_is( <<'CODE', <<'OUTPUT', "push_eh - throw" );
51     print "main\n"
52     push_eh _handler
53     new P30, 'Exception'
54     throw P30
55     print "not reached\n"
56     end
57 _handler:
58     print "caught it\n"
59     end
60 CODE
61 main
62 caught it
63 OUTPUT
65 pasm_output_is( <<'CODE', <<'OUTPUT', "push_eh eh - throw" );
66     print "main\n"
67     new P29, 'Exception_Handler'
68     set_addr P29, _handler
69     push_eh P29
70     new P30, 'Exception'
71     throw P30
72     print "not reached\n"
73     end
74 _handler:
75     print "caught it\n"
76     end
77 CODE
78 main
79 caught it
80 OUTPUT
82 pasm_output_is( <<'CODE', <<'OUTPUT', "get_results" );
83     print "main\n"
84     push_eh handler
85     new P1, 'Exception'
86     set P1[0], "just pining"
87     throw P1
88     print "not reached\n"
89     end
90 handler:
91     get_results "0,0", P0, S0
92     print "caught it\n"
93     typeof S1, P0
94     print S1
95     print "\n"
96     print S0
97     print "\n"
98     null P5
99     end
101 CODE
102 main
103 caught it
104 Exception
105 just pining
106 OUTPUT
108 pasm_output_is( <<'CODE', <<'OUTPUT', "get_results - be sure registers are ok" );
109 # see also #38459
110     print "main\n"
111     new P0, 'Integer'
112     push_eh handler
113     new P1, 'Exception'
114     set P1[0], "just pining"
115     throw P1
116     print "not reached\n"
117     end
118 handler:
119     get_results "0,0", P1, S0
120     inc P0
121     print "ok\n"
122     end
124 CODE
125 main
127 OUTPUT
129 pir_output_is( <<'CODE', <<'OUTPUT', ".get_results() - PIR" );
130 .sub main :main
131     print "main\n"
132     push_eh _handler
133     new P1, 'Exception'
134     set P1[0], "just pining"
135     throw P1
136     print "not reached\n"
137     end
138 _handler:
139     .local pmc e
140     .local string s
141     .get_results (e, s)
142     print "caught it\n"
143     typeof S1, e
144     print S1
145     print "\n"
146     print s
147     print "\n"
148     null P5
149 .end
150 CODE
151 main
152 caught it
153 Exception
154 just pining
155 OUTPUT
157 pasm_output_is( <<'CODE', <<'OUTPUT', "push_eh - throw - message" );
158     print "main\n"
159     push_eh _handler
161     new P30, 'Exception'
162     set P30["_message"], "something happend"
163     throw P30
164     print "not reached\n"
165     end
166 _handler:
167     get_results "0,0", P5, S0
168     print "caught it\n"
169     print S0
170     print "\n"
171     end
172 CODE
173 main
174 caught it
175 something happend
176 OUTPUT
178 pasm_error_output_like( <<'CODE', <<'OUTPUT', "throw - no handler" );
179     new P0, 'Exception'
180     set P0["_message"], "something happend"
181     throw P0
182     print "not reached\n"
183     end
184 CODE
185 /something happend/
186 OUTPUT
188 pasm_error_output_like( <<'CODE', <<'OUTPUT', "throw - no handler, no message" );
189     push_eh _handler
190     new P0, 'Exception'
191     pop_eh
192     throw P0
193     print "not reached\n"
194     end
195 _handler:
196     end
197 CODE
198 /No exception handler and no message/
199 OUTPUT
201 pasm_error_output_like( <<'CODE', <<'OUTPUT', "throw - no handler, no message" );
202     new P0, 'Exception'
203     throw P0
204     print "not reached\n"
205     end
206 CODE
207 /No exception handler and no message/
208 OUTPUT
210 pasm_output_is( <<'CODE', <<'OUTPUT', "2 exception handlers" );
211     print "main\n"
212     push_eh _handler1
213     push_eh _handler2
215     new P30, 'Exception'
216     set P30["_message"], "something happend"
217     throw P30
218     print "not reached\n"
219     end
220 _handler1:
221     get_results "0,0", P5, S0
222     print "caught it in 1\n"
223     print S0
224     print "\n"
225     end
226 _handler2:
227     get_results "0,0", P0, S0
228     print "caught it in 2\n"
229     print S0
230     print "\n"
231     end
232 CODE
233 main
234 caught it in 2
235 something happend
236 OUTPUT
238 pasm_output_is( <<'CODE', <<'OUTPUT', "2 exception handlers, throw next" );
239     print "main\n"
240     push_eh _handler1
241     push_eh _handler2
243     new P30, 'Exception'
244     set P30["_message"], "something happend"
245     throw P30
246     print "not reached\n"
247     end
248 _handler1:
249     get_results "0,0", P5, S0
250     print "caught it in 1\n"
251     print S0
252     print "\n"
253     end
254 _handler2:
255     get_results "0,0", P5, S0
256     print "caught it in 2\n"
257     print S0
258     print "\n"
259     throw P5    # XXX rethrow?
260     end
261 CODE
262 main
263 caught it in 2
264 something happend
265 caught it in 1
266 something happend
267 OUTPUT
269 pasm_output_is( <<'CODE', <<OUT, "die" );
270     push_eh _handler
271     die 3, 100
272     print "not reached\n"
273     end
274 _handler:
275     print "caught it\n"
276     end
277 CODE
278 caught it
281 pasm_output_is( <<'CODE', <<OUT, "die, error, severity" );
282     push_eh _handler
283     die 3, 100
284     print "not reached\n"
285     end
286 _handler:
287     get_results "0,0", P5, S0
288     print "caught it\n"
289     set I0, P5["_type"]
290     print "error "
291     print I0
292     print "\n"
293     set I0, P5["_severity"]
294     print "severity "
295     print I0
296     print "\n"
297     end
298 CODE
299 caught it
300 error 100
301 severity 3
304 pasm_error_output_like( <<'CODE', <<OUT, "die - no handler" );
305     die 3, 100
306     print "not reached\n"
307     end
308 _handler:
309     print "caught it\n"
310     end
311 CODE
312 /No exception handler and no message/
315 pasm_output_is( <<'CODE', '', "exit exception" );
316     noop
317     exit 0
318     print "not reached\n"
319     end
320 CODE
322 pasm_output_is( <<'CODE', <<'OUTPUT', "push_eh - throw" );
323     print "main\n"
324     push_eh handler
325     print "ok\n"
326     new P30, 'Exception'
327     throw P30
328     print "not reached\n"
329     end
330 handler:
331     print "caught it\n"
332     end
333 CODE
334 main
336 caught it
337 OUTPUT
340 pasm_output_is( <<'CODE', <<'OUTPUT', "pushmark" );
341     pushmark 10
342     print "ok 1\n"
343     popmark 10
344     print "ok 2\n"
345     end
346 CODE
347 ok 1
348 ok 2
349 OUTPUT
351 pasm_output_is( <<'CODE', <<'OUTPUT', "pushmark nested" );
352     pushmark 10
353     pushmark 11
354     print "ok 1\n"
355     popmark 11
356     popmark 10
357     print "ok 2\n"
358     end
359 CODE
360 ok 1
361 ok 2
362 OUTPUT
364 pasm_error_output_like( <<'CODE', <<'OUTPUT', "pushmark - pop wrong one" );
365     pushmark 10
366     print "ok 1\n"
367     popmark 500
368     print "never\n"
369     end
370 CODE
371 /Mark 500 not found/
372 OUTPUT
374 pasm_output_is( <<'CODE', <<'OUTPUT', "pushaction, throw" );
375     push_eh handler
376     print "ok 1\n"
377     .const .Sub P10 = "action"
378     pushaction P10
379     print "ok 2\n"
380     new P10, 'Exception'
381     throw P10
382     print "never\n"
383 handler:
384     print "ok 3\n"
385     end
386 .pcc_sub action:
387     get_params "0", I5
388     print "in action I5 = "
389     print I5
390     print "\n"
391     returncc
392 CODE
393 ok 1
394 ok 2
395 in action I5 = 1
396 ok 3
397 OUTPUT
399 pir_output_is( <<'CODE', <<'OUTPUT', 'cleanup global:  continuation' );
400 .sub main :main
401     .local pmc outer, cont
402     outer = new 'String'
403     outer = "Outer value\n"
404     set_global ['Foo'; 'Bar'], "test", outer
405     new cont, 'Continuation'
406     set_addr cont, endcont
407     set_global ['Foo'; 'Bar'], "exit", cont
408     show_value()
409     test1()
410     print "skipped.\n"
411 endcont:
412     show_value()
413 .end
414 .sub test1
415     .local pmc test1_binding, old_value, cleanup
416     .lex "old_value", old_value
417     test1_binding = new 'String'
418     test1_binding = "Inner value\n"
419     old_value = get_global ['Foo'; 'Bar'], "test"
420     .const .Sub test1_cleanup_sub = "test1_cleanup"
421     cleanup = newclosure test1_cleanup_sub
422     pushaction cleanup
423     set_global ['Foo'; 'Bar'], "test", test1_binding
424     show_value()
425     test2()
426     show_value()
427 .end
428 .sub test1_cleanup :outer(test1)
429     .local pmc old_value
430     print "[in test1_cleanup]\n"
431     find_lex old_value, "old_value"
432     set_global ['Foo'; 'Bar'], "test", old_value
433 .end
434 .sub test2
435     .local pmc test2_binding, exit
436     test2_binding = new 'String'
437     test2_binding = "Innerer value\n"
438     set_global ['Foo'; 'Bar'], "test", test2_binding
439     show_value()
440     exit = get_global ['Foo'; 'Bar'], "exit"
441     exit()
442 .end
443 .sub show_value
444     .local pmc value
445     value = get_global ['Foo'; 'Bar'], "test"
446     print value
447 .end
448 CODE
449 Outer value
450 Inner value
451 Innerer value
452 [in test1_cleanup]
453 Outer value
454 OUTPUT
456 pir_output_is( <<'CODE', <<'OUTPUT', 'cleanup global:  throw' );
457 .sub main :main
458     .local pmc outer
459     outer = new 'String'
460     outer = "Outer value\n"
461     set_global ['Foo'; 'Bar'], "test", outer
462     push_eh eh
463     show_value()
464     test1()
465     print "skipped.\n"
467     .local pmc exception
468     .get_results (exception, $S0)
469     print "Error: "
470     print exception
471     print "\n"
472 last:
473     show_value()
474 .end
475 .sub test1
476     .local pmc test1_binding, old_value, cleanup
477     .lex "old_value", old_value
478     test1_binding = new 'String'
479     test1_binding = "Inner value\n"
480     old_value = get_global ['Foo'; 'Bar'], "test"
481     .const .Sub test1_cleanup_sub = "test1_cleanup"
482     cleanup = newclosure test1_cleanup_sub
483     pushaction cleanup
484     set_global ['Foo'; 'Bar'], "test", test1_binding
485     show_value()
486     test2()
487     show_value()
488 .end
489 .sub test1_cleanup :outer(test1)
490     .local pmc old_value
491     print "[in test1_cleanup]\n"
492     find_lex old_value, "old_value"
493     set_global ['Foo'; 'Bar'], "test", old_value
494 .end
495 .sub test2
496     .local pmc test2_binding, exit
497     test2_binding = new 'String'
498     test2_binding = "Innerer value\n"
499     set_global ['Foo'; 'Bar'], "test", test2_binding
500     show_value()
501     exit = new 'Exception'
502     exit["_message"] = "something happened"
503     throw exit
504 .end
505 .sub show_value
506     .local pmc value
507     value = get_global ['Foo'; 'Bar'], "test"
508     print value
509 .end
510 CODE
511 Outer value
512 Inner value
513 Innerer value
514 [in test1_cleanup]
515 Error: something happened
516 Outer value
517 OUTPUT
519 pir_error_output_like( <<'CODE', <<'OUTPUT', 'pop_eh out of context (1)' );
520 .sub main :main
521     pushmark 1
522     pop_eh
523     print "no exceptions.\n"
524 .end
525 CODE
526 /No exception to pop./
527 OUTPUT
529 pir_output_is( <<'CODE', <<'OUTPUT', 'pop_eh out of context (2)' );
530 .sub main :main
531     .local pmc outer, cont
532     push_eh handler
533     test1()
534     print "skipped.\n"
535     goto done
536 handler:
537     .local pmc exception
538     .get_results (exception, $S0)
539     print "Error: "
540     print $S0
541     print "\n"
542 done:
543     print "done.\n"
544 .end
545 .sub test1
546     .local pmc exit
547     print "[in test1]\n"
548     ## pop_eh is illegal here, and signals an exception.
549     pop_eh
550     print "[cleared]\n"
551 .end
552 CODE
553 [in test1]
554 Error: No exception to pop.
555 done.
556 OUTPUT
558 # stringification is handled by a vtable method, which runs in a second
559 # runloop. when an error in the method tries to go to a Error_Handler defined
560 # outside it, it winds up going to the inner runloop, giving strange results.
561 pir_output_is( <<'CODE', <<'OUTPUT', 'pop_eh out of context (2)', todo => 'runloop shenanigans' );
562 .sub main :main
563         $P0 = get_hll_global ['Foo'], 'load'
564         $P0()
565         $P0 = new 'Foo'
566         push_eh catch
567         $S0 = $P0
568         pop_eh
569         say "huh?"
570         .return()
572 catch:
573         say "caught"
574         .return()
575 .end
577 .namespace ['Foo']
579 .sub load
580     $P0 = newclass 'Foo'
581 .end
583 .sub get_string :vtable :method
584     $P0 = new 'Exception'
585     throw $P0
586 .end
587 CODE
588 caught
589 OUTPUT
591 pir_error_output_like( <<'CODE', <<'OUTPUT', "pushaction - throw in main" );
592 .sub main :main
593     print "main\n"
594     .const .Sub at_exit = "exit_handler"
595     pushaction at_exit
596     $P0 = new 'Exception'
597     throw $P0
598     .return()
599 .end
601 .sub exit_handler
602     .param int flag
603     print "at_exit, flag = "
604     say flag
605 .end
606 CODE
607 /^main
608 No exception handler/
609 OUTPUT
611 # exception handlers are still run in an inferior runloop, which messes up
612 # nonlocal exit from within handlers.
613 pir_output_like(
614     <<'CODE', <<'OUTPUT', "pushaction: error while handling error", todo => 'runloop shenanigans' );
615 .sub main :main
616     push_eh h
617     print "main\n"
618     .const .Sub at_exit = "exit_handler"
619     pushaction at_exit
620     $P1 = new 'Exception'
621     throw $P1
622     print "never 1\n"
624     ## this is never actually reached, because exit_handler throws an unhandled
625     ## exception before the handler is entered.
626     print "in outer handler\n"
627 .end
629 .sub exit_handler :outer(main)
630     .param int flag
631     print "at_exit, flag = "
632     say flag
633     $P2 = new 'Exception'
634     throw $P2
635     print "never 2\n"
636 .end
637 CODE
638 /^main
639 at_exit, flag = 1
640 No exception handler/
641 OUTPUT
643 pir_output_is( <<'CODE', <<'OUTPUT', "exit_handler via exit exception" );
644 .sub main :main
645     .local pmc a
646     .lex 'a', a
647     a = new 'Integer'
648     a = 42
649     push_eh handler
650     exit 0
651 handler:
652     .return exit_handler()
653 .end
655 .sub exit_handler :outer(main)
656     say "at_exit"
657     .local pmc a
658     a = find_lex 'a'
659     print 'a = '
660     say a
661 .end
662 CODE
663 at_exit
664 a = 42
665 OUTPUT
667 ## Regression test for r14697.  This probably won't be needed when PDD23 is
668 ## fully implemented.
669 pir_error_output_like( <<'CODE', <<'OUTPUT', "invoke handler in calling sub" );
670 ## This tests that error handlers are out of scope when invoked (necessary for
671 ## rethrow) when the error is signalled in another sub.
672 .sub main :main
673     push_eh handler
674     broken()
675     print "not reached.\n"
676 handler:
677     .local pmc exception
678     .get_results (exception, $S0)
679     print "in handler.\n"
680     print $S0
681     print "\n"
682     rethrow exception
683 .end
685 .sub broken
686     $P0 = new 'Exception'
687     $P0["_message"] = "something broke"
688     throw $P0
689 .end
690 CODE
691 /\Ain handler.
692 something broke
693 something broke
694 current inst/
695 OUTPUT
697 pir_error_output_like( <<'CODE', <<'OUTPUT', 'die_s' );
698 .sub main :main
699     die 'We are dying str!'
700 .end
701 CODE
702 /We are dying str!/
703 OUTPUT
704 pir_error_output_like( <<'CODE', <<'OUTPUT', 'die_p' );
705 .sub main :main
706     .local pmc msg
707     msg = new 'String'
708     msg = 'We are dying pmc!'
709     die msg
710 .end
711 CODE
712 /We are dying pmc!/
713 OUTPUT
715 # Local Variables:
716 #   mode: cperl
717 #   cperl-indent-level: 4
718 #   fill-column: 100
719 # End:
720 # vim: expandtab shiftwidth=4: