4 # Tests derived from Japhs.
6 # These test use obscure features of Perl, or surprising combinations
7 # of features. The tests were added because in the past, they have
8 # exposed several bugs in Perl.
10 # Some of these tests may actually (mis)use bugs or use undefined behaviour.
11 # These tests are still useful - behavioural changes or bugfixes will be
12 # noted, and a remark can be put in the documentation. (Don't forget to
15 # Getting everything to run well on the myriad of platforms Perl runs on
16 # is unfortunately not a trivial task.
18 # WARNING: these tests are obfuscated. Do not get frustrated.
19 # Ask Abigail <abigail@abigail.be>, or use the Deparse or Concise
20 # modules (the former parses Perl to Perl, the latter shows the
21 # op syntax tree) like this:
22 # ./perl -Ilib -MO=Deparse foo.pl
23 # ./perl -Ilib -MO=Concise foo.pl
30 skip_all('EBCDIC') if $::IS_EBCDIC;
35 # ./test.pl does real evilness by jumping to a label.
36 # This function copies the skip from ./test, omitting the goto.
40 my $n = @_ ? shift : 1;
43 print STDOUT "ok $test # skip: $why\n";
50 # ./test.pl doesn't give use 'notok', so we make it here.
53 my ($pass, $name, @mess) = @_;
54 _ok(!$pass, _where(), $name, @mess);
57 my $JaPH = "Just another Perl Hacker";
58 my $JaPh = "Just another Perl hacker";
59 my $JaPH_n = "Just another Perl Hacker\n";
60 my $JaPh_n = "Just another Perl hacker\n";
61 my $JaPH_s = "Just another Perl Hacker ";
62 my $JaPh_s = "Just another Perl hacker ";
63 my $JaPH_c = "Just another Perl Hacker,";
64 my $JaPh_c = "Just another Perl hacker,";
69 my $out = sprintf "Just another Perl Hacker";
75 my @primes = (2, 3, 7, 13, 53, 101, 557, 1429);
76 my @composites = (4, 10, 25, 32, 75, 143, 1333, 1728);
78 my %primeness = ((map {$_ => 1} @primes),
79 (map {$_ => 0} @composites));
81 while (my ($num, $is_prime) = each %primeness) {
82 my $comment = "$num is " . ($is_prime ? "prime." : "composite.");
84 my $sub = $is_prime ? "ok" : "notok";
86 &$sub (( 1 x $num) !~ /^1?$|^(11+?)\1+$/, $comment);
87 &$sub (( 0 x $num) !~ m 0^\0?$|^(\0\0+?)\1+$0, $comment);
88 &$sub (("m" x $num) !~ m m^\m?$|^(\m\m+?)\1+$mm, $comment);
93 { # Some platforms use different quoting techniques.
94 # I do not have access to those platforms to test
95 # things out. So, we'll skip things....
96 if ($^O eq 'MSWin32' ||
98 skip "Your platform quotes differently.", 3;
102 my $expected = $JaPH;
103 $expected =~ s/ /\n/g;
105 is (runperl (switches => [qw /'-weprint<<EOT;' -eJust -eanother
106 -ePerl -eHacker -eEOT/],
108 $expected, "Multiple -e switches");
110 is (runperl (switches => [q !'-wle$_=<<EOT;y/\n/ /;print;'!,
111 qw ! -eJust -eanother -ePerl -eHacker -eEOT!],
113 $JaPH . " \n", "Multiple -e switches");
115 is (runperl (switches => [qw !-wl!],
116 progs => [qw !print qq-@{[ qw+ Just
117 another Perl Hacker +]}-!],
119 $JaPH_n, "Multiple -e switches");
123 if ($^O eq 'MSWin32' ||
125 skip "Your platform quotes differently.", 1;
128 is (runperl (switches => [qw /-sweprint --/,
129 "-_='Just another Perl Hacker'"],
132 $JaPH, 'setting $_ via -s');
136 my $datafile = "datatmp000";
137 1 while -f ++ $datafile;
138 END {unlink_all $datafile if $datafile}
140 open MY_DATA, "> $datafile" or die "Failed to open $datafile: $!";
141 print MY_DATA << " --";
149 close MY_DATA or die "Failed to close $datafile: $!\n";
156 if (/^#{7}(?:\s+(.*))?/) {
157 push @progs => {COMMENT => $1 || '',
165 elsif (/^(COMMENT|CODE|ARGS|SWITCHES|EXPECT|SKIP|SKIP_OS)
168 $progs [-1] {$key} = '' unless exists $progs [-1] {$key};
169 next unless defined $2;
176 if (ref ($progs [-1] {$key})) {
177 push @{$progs [-1] {$key}} => $_;
180 $progs [-1] {$key} .= $_;
184 foreach my $program (@progs) {
185 if (exists $program -> {SKIP}) {
186 chomp $program -> {SKIP};
187 skip $program -> {SKIP}, 1;
191 chomp @{$program -> {SKIP_OS}};
192 if (@{$program -> {SKIP_OS}}) {
193 if (grep {$^O eq $_} @{$program -> {SKIP_OS}}) {
194 skip "Your OS uses different quoting.", 1;
199 map {s/\$datafile/$datafile/} @{$program -> {ARGS}};
200 $program -> {EXPECT} = $JaPH unless exists $program -> {EXPECT};
201 $program -> {EXPECT} =~ s/\$JaPH_s\b/$JaPH_s/g;
202 $program -> {EXPECT} =~ s/\$JaPh_c\b/$JaPh_c/g;
203 $program -> {EXPECT} =~ s/\$JaPh\b/$JaPh/g;
204 chomp ($program -> {EXPECT}, @{$program -> {SWITCHES}},
205 @{$program -> {ARGS}});
206 fresh_perl_is ($program -> {CODE},
207 $program -> {EXPECT},
208 {switches => $program -> {SWITCHES},
209 args => $program -> {ARGS},
211 $program -> {COMMENT});
216 my $progfile = "progtmp000";
217 1 while -f ++ $progfile;
218 END {unlink_all $progfile if $progfile}
220 my @programs = (<< ' --', << ' --');
222 BEGIN{$|=$SIG{__WARN__}=sub{$_=$_[0];y-_- -;print/(.)"$/;seek _,-open(_
223 ,"+<$0"),2;truncate _,tell _;close _;exec$0}}//rekcaH_lreP_rehtona_tsuJ
226 BEGIN{$SIG{__WARN__}=sub{$_=pop;y-_- -;print/".*(.)"/;
227 truncate$0,-1+-s$0;exec$0;}}//rekcaH_lreP_rehtona_tsuJ
231 if ($^O eq 'VMS' or $^O eq 'MSWin32') {
232 # VMS needs extensions for files to be executable,
233 # but the Japhs above rely on $0 being exactly the
234 # filename of the program.
235 skip $^O, 2 * @programs;
240 unless (defined $Config {useperlio}) {
241 skip "Uuseperlio", 2 * @programs;
246 foreach my $program (@programs) {
247 open my $fh => "> $progfile" or die "Failed to open $progfile: $!\n";
249 close $fh or die "Failed to close $progfile: $!\n";
251 chmod 0755 => $progfile or die "Failed to chmod $progfile: $!\n";
252 my $command = "./$progfile 2>&1";
253 if ( $^O eq 'qnx' ) {
254 skip "#!./perl not supported in QNX4";
255 skip "#!./perl not supported in QNX4";
257 my $output = `$command`;
259 is ($output, $JaPH, "Self correcting code $i");
261 $output = `$command`;
262 is ($output, "", "Self corrected code $i");
269 ####### Funky loop 1.
270 $_ = q ;4a75737420616e6f74686572205065726c204861636b65720as;;
271 for (s;s;s;s;s;s;s;s;s;s;s;s)
272 {s;(..)s?;qq qprint chr 0x$1 and \161 ssq;excess;}
274 ####### Funky loop 2.
275 $_ = q *4a75737420616e6f74686572205065726c204861636b65720a*;
276 for ($*=******;$**=******;$**=******) {$**=*******s*..*qq}
279 SKIP: $* was removed.
281 ####### Funky loop 3.
282 $_ = q *4a75737420616e6f74686572205065726c204861636b65720a*;
283 for ($*=******;$**=******;$**=******) {$**=*******s*..*qq}
286 SKIP: $* was removed.
288 ####### Funky loop 4.
289 $_ = q ?4a75737420616e6f74686572205065726c204861636b65720as?;??;
291 {??;s;(..)s?;qq ?print chr 0x$1 and \161 ss?;excess;??}
292 SKIP: Abuses a fixed bug.
294 ####### Funky loop 5.
295 for (s??4a75737420616e6f74686572205065726c204861636b65720as?;??;??)
296 {s?(..)s\??qq \?print chr 0x$1 and q ss\??excess}
297 SKIP: Abuses a fixed bug.
299 ####### Funky loop 6.
300 $a = q 94a75737420616e6f74686572205065726c204861636b65720a9 and
301 ${qq$\x5F$} = q 97265646f9 and s g..g;
302 qq e\x63\x68\x72\x20\x30\x78$&eggee;
303 {eval if $a =~ s e..eqq qprint chr 0x$& and \x71\x20\x71\x71qeexcess}
306 @r=reverse(M=>(0)x99=>CM=>(0)x399=>D=>(0)x99=>CD=>(
307 0)x299=>C=>(0)x9=>XC=>(0)x39=>L=>(0)x9=>XL=>(0)x29=>X=>IX=>0=>0=>0=>V=>IV=>0=>0
308 =>I=>$==-2449231+gm_julian_day+time);do{until($=<$#r){$_.=$r[$#r];$=-=$#r}for(;
309 !$r[--$#r];){}}while$=;$,="\x20";print+$_=>September=>MCMXCIII=>=>=>=>=>=>=>=>
313 SKIP: Times::JulianDay not part of the main distribution.
316 sub _'_{$_'_=~s/$a/$_/}map{$$_=$Z++}Y,a..z,A..X;*{($_::_=sprintf+q=%X==>"$A$Y".
317 "$b$r$T$u")=~s~0~O~g;map+_::_,U=>T=>L=>$Z;$_::_}=*_;sub _{print+/.*::(.*)/s};;;
318 *{chr($b*$e)}=*_'_;*__=*{chr(1<<$e)}; # Perl 5.6.0 broke this...
319 _::_(r(e(k(c(a(H(__(l(r(e(P(__(r(e(h(t(o(n(a(__(t(us(J())))))))))))))))))))))))
320 EXPECT: Just__another__Perl__Hacker
323 $"=$,;*{;qq{@{[(A..Z)[qq[0020191411140003]=~m[..]g]]}}}=*_=sub{print/::(.*)/};
324 $\=$/;q<Just another Perl Hacker>->();
327 $"=$,;*{;qq{@{[(A..Z)[qq[0020191411140003]=~m[..]g]]}}}=*_;
328 sub _ {push @_ => /::(.*)/s and goto &{ shift}}
329 sub shift {print shift; @_ and goto &{+shift}}
330 Hack ("Just", "Perl ", " ano", "er\n", "ther "); # YYYYMMDD
333 $, = " "; sub AUTOLOAD {($AUTOLOAD =~ /::(.*)/) [0];}
334 print+Just (), another (), Perl (), Hacker ();
336 ####### Look ma! No letters!
337 $@="\145\143\150\157\040\042\112\165\163\164\040\141\156\157\164".
338 "\150\145\162\040\120\145\162\154\040\110\141\143\153\145\162".
339 "\042\040\076\040\057\144\145\166\057\164\164\171";`$@`
342 ####### sprintf fun 1.
343 sub f{sprintf$_[0],$_[1],$_[2]}print f('%c%s',74,f('%c%s',117,f('%c%s',115,f(
344 '%c%s',116,f('%c%s',32,f('%c%s',97,f('%c%s',0x6e,f('%c%s',111,f('%c%s',116,f(
345 '%c%s',104,f('%c%s',0x65,f('%c%s',114,f('%c%s',32,f('%c%s',80,f('%c%s',101,f(
346 '%c%s',114,f('%c%s',0x6c,f('%c%s',32,f('%c%s',0x48,f('%c%s',97,f('%c%s',99,f(
347 '%c%s',107,f('%c%s',101,f('%c%s',114,f('%c%s',10,)))))))))))))))))))))))))
349 ####### sprintf fun 2.
350 sub f{sprintf'%c%s',$_[0],$_[1]}print f(74,f(117,f(115,f(116,f(32,f(97,
351 f(110,f(111,f(116,f(104,f(0x65,f(114,f(32,f(80,f(101,f(114,f(0x6c,f(32,
352 f(0x48,f(97,f(99,f(107,f(101,f(114,f(10,q ff)))))))))))))))))))))))))
355 %0=map{local$_=$_;reverse+chop,$_}ABC,ACB,BAC,BCA,CAB,CBA;$_=3 .AC;1while+
356 s/(\d+)((.)(.))/($0=$1-1)?"$0$3$0{$2}1$2$0$0{$2}$4":"$3 => $4\n"/xeg;print
411 -Mstrict='}); print "Just another Perl Hacker"; ({'
413 SKIP: No longer works in 5.8.2 and beyond.
417 srand 123456;$-=rand$_--=>@[[$-,$_]=@[[$_,$-]for(reverse+1..(@[=split
418 //=>"IGrACVGQ\x02GJCWVhP\x02PL\x02jNMP"));print+(map{$_^q^"^}@[),"\n"
419 SKIP: Solaris specific.
421 ####### print and __PACKAGE__
422 package Just_another_Perl_Hacker; sub print {($_=$_[0])=~ s/_/ /g;
423 print } sub __PACKAGE__ { &
424 print ( __PACKAGE__)} &
429 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
430 / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
431 % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %;
432 BEGIN {% % = ($ _ = " " => print "Just another Perl Hacker\n")}
435 sub J::FETCH{Just }$_.='print+"@{[map';sub J::TIESCALAR{bless\my$J,J}
436 sub A::FETCH{another}$_.='{tie my($x),$';sub A::TIESCALAR{bless\my$A,A}
437 sub P::FETCH{Perl }$_.='_;$x}qw/J A P';sub P::TIESCALAR{bless\my$P,P}
438 sub H::FETCH{Hacker }$_.=' H/]}\n"';eval;sub H::TIESCALAR{bless\my$H,H}
441 package Z;use overload'""'=>sub{$b++?Hacker:another};
442 sub TIESCALAR{bless\my$y=>Z}sub FETCH{$a++?Perl:Just}
443 $,=$";my$x=tie+my$y=>Z;print$y,$x,$y,$x,"\n";#Abigail
447 sub A::TIESCALAR{bless\my$x=>A};package B;@q[0..3]=qw/Hacker Perl
448 another Just/;use overload'""'=>sub{pop @q};sub A::FETCH{bless\my
449 $y=>B}; tie my $shoe => qq 'A';print "$shoe $shoe $shoe $shoe\n";
452 sub A::TIESCALAR{bless\my$x=>'A'};package B;@q=qw/Hacker Perl
453 another Just/;use overload'""',sub{pop @q};sub A::FETCH{bless
454 \my $y=>B};tie my$shoe=>'A';print"$shoe $shoe $shoe $shoe\n";
457 tie $" => A; $, = " "; $\ = "\n"; @a = ("") x 2; print map {"@a"} 1 .. 4;
458 sub A::TIESCALAR {bless \my $A => A} # Yet Another silly JAPH by Abigail
459 sub A::FETCH {@q = qw /Just Another Perl Hacker/ unless @q; shift @q}
460 SKIP: Pending a bug fix.
462 ####### Prototype fun 1
463 sub camel (^#87=i@J&&&#]u'^^s]#'#={123{#}7890t[0.9]9@+*`"'***}A&&&}n2o}00}t324i;
464 h[{e **###{r{+P={**{e^^^#'#i@{r'^=^{l+{#}H***i[0.9]&@a5`"':&^;&^,*&^$43##@@####;
465 c}^^^&&&k}&&&}#=e*****[]}'r####'`=437*{#};::'1[0.9]2@43`"'*#==[[.{{],,,1278@#@);
466 print+((($llama=prototype'camel')=~y|+{#}$=^*&[0-9]i@:;`"',.| |d)&&$llama."\n");
467 SKIP: Abuses a fixed bug.
469 ####### Prototype fun 2
470 print prototype sub "Just another Perl Hacker" {};
471 SKIP: Abuses a fixed bug.
473 ####### Prototype fun 3
474 sub _ "Just another Perl Hacker"; print prototype \&_
475 SKIP: Abuses a fixed bug.
479 ${"@_"} = "/"; split // => eval join "+" => 1 .. 7;
480 *{"@_"} = sub {foreach (sort keys %_) {print "$_ $_{$_} "}};
481 %{"@_"} = %_ = (Just => another => Perl => Hacker); &{%{%_}};
482 SKIP: Hashes are now randomized.
486 $" = "/"; split // => eval join "+" => 1 .. 7;
487 *{"@_"} = sub {foreach (sort keys %_) {print "$_ $_{$_} "}};
488 %_ = (Just => another => Perl => Hacker); &{%_};
489 SKIP: Hashes are now randomized.
493 $" = "/"; split $, => eval join "+" => 1 .. 7;
494 *{"@_"} = sub {foreach (sort keys %_) {print "$_ $_{$_} "}};
495 %{"@_"} = %_ = (Just => another => Perl => Hacker); &{%{%_}};
496 SKIP: Hashes are now randomized.
499 ####### Here documents 1
500 $_ = "\x3C\x3C\x45\x4F\x54"; s/<<EOT/<<EOT/e; print;
501 Just another Perl Hacker
504 ####### Here documents 2
505 $_ = "\x3C\x3C\x45\x4F\x54";
506 print if s/<<EOT/<<EOT/e;
507 Just another Perl Hacker
510 ####### Here documents 3
511 $_ = "\x3C\x3C\x45\x4F\x54" and s/<<EOT/<<EOT/e and print;
512 Just another Perl Hacker
515 ####### Here documents 4
516 $_ = "\x3C\x3C\x45\x4F\x54\n" and s/<<EOT/<<EOT/ee and print;
517 "Just another Perl Hacker"
520 ####### Self modifying code 1
521 $_ = "goto F.print chop;\n=rekcaH lreP rehtona tsuJ";F1:eval
524 ####### Overloaded constants 1
525 BEGIN {$^H {q} = sub {pop and pop and print pop}; $^H = 2**4.2**12}
526 "Just "; "another "; "Perl "; "Hacker";
529 ####### Overloaded constants 2
530 BEGIN {$^H {q} = sub {$_ [1] =~ y/S-ZA-IK-O/q-tc-fe-m/d; $_ [1]}; $^H = 0x28100}
531 print "Just another PYTHON hacker\n";
534 ####### Overloaded constants 3
535 BEGIN {$^H {join "" => ("a" .. "z") [8, 13, 19, 4, 6, 4, 17]} = sub
536 {["", "Just ", "another ", "Perl ", "Hacker\n"] -> [shift]};
537 $^H = hex join "" => reverse map {int ($_ / 2)} 0 .. 4}
540 ####### Overloaded constants 4
541 BEGIN {$^H {join "" => ("a" .. "z") [8, 13, 19, 4, 6, 4, 17]} = sub
542 {["", "Just ", "another ", "Perl ", "Hacker"] -> [shift]};
543 $^H = hex join "" => reverse map {int ($_ / 2)} 0 .. 4}
544 print 1, 2, 3, 4, "\n";
546 ####### Overloaded constants 5
547 BEGIN {my $x = "Knuth heals rare project\n";
548 $^H {integer} = sub {my $y = shift; $_ = substr $x => $y & 0x1F, 1;
549 $y > 32 ? uc : lc}; $^H = hex join "" => 2, 1, 1, 0, 0}
550 print 52,2,10,23,16,8,1,19,3,6,15,12,5,49,21,14,9,11,36,13,22,32,7,18,24;
553 print v74.117.115.116.32;
554 print v97.110.111.116.104.101.114.32;
555 print v80.101.114.108.32;
556 print v72.97.99.107.101.114.10;
559 print 74.117.115.116.32;
560 print 97.110.111.116.104.101.114.32;
561 print 80.101.114.108.32;
562 print 72.97.99.107.101.114.10;
565 print v74.117.115.116.32, v97.110.111.116.104.101.114.32,
566 v80.101.114.108.32, v72.97.99.107.101.114.10;
569 print 74.117.115.116.32, 97.110.111.116.104.101.114.32,
570 80.101.114.108.32, 72.97.99.107.101.114.10;
573 print v74.117.115.116.32.97.110.111.116.104.101.114.
574 v32.80.101.114.108.32.72.97.99.107.101.114.10;
577 print 74.117.115.116.32.97.110.111.116.104.101.114.
578 32.80.101.114.108.32.72.97.99.107.101.114.10;
580 ####### Symbolic references.
581 map{${+chr}=chr}map{$_=>$_^ord$"}$=+$]..3*$=/2;
582 print "$J$u$s$t $a$n$o$t$h$e$r $P$e$r$l $H$a$c$k$e$r\n";
587 $; # The return of the lone dollar?
588 {Just=>another=>Perl=>Hacker=>} # Bare block?
590 print%; # No right operand for %?
593 @;=split//=>"Joel, Preach sartre knuth\n";$;=chr 65;%;=map{$;++=>$_}
594 0,22,13,16,5,14,21,1,23,11,2,7,12,6,8,15,3,19,24,14,10,20,18,17,4,25
599 $;=$";$;{Just=>another=>Perl=>Hacker=>}=$/;print%;
602 $_ = "\112\165\163\1648\141\156\157\164\150\145\1628\120\145"
603 . "\162\1548\110\141\143\153\145\162\0128\177" and &japh;
604 sub japh {print "@_" and return if pop; split /\d/ and &japh}
605 SKIP: As of 5.12.0, split() in void context no longer populates @_.
608 sub _ {$_ = shift and y/b-yB-Y/a-yB-Y/ xor !@ _?
610 print and push @_ => shift and goto &{(caller (0)) [3]}}
611 split // => "KsvQtbuf fbsodpmu\ni flsI " xor & _
612 SKIP: As of 5.12.0, split() in void context no longer populates @_.
615 :$:=~s:$":Just$&another$&:;$:=~s:
616 :Perl$"Hacker$&:;chop$:;print$:#:
620 -:;another Perl Hacker
628 -:;another Perl Hacker
630 $:;$:=~y:;::d;print+Just.$:
633 s[$,][join$,,(split$,,($!=85))[(q[0006143730380126152532042307].
634 q[41342211132019313505])=~m[..]g]]e and y[yIbp][HJkP] and print;
635 SKIP: Platform dependent.
638 eval {die ["Just another Perl Hacker"]}; print ${$@}[$#{@${@}}]
641 eval {die ["Just another Perl Hacker\n"]}; print ${$@}[$#{@${@}}]
644 eval {die ["Just another Perl Hacker"]}; print ${${@}}[$#{@{${@}}}]
647 eval {die ["Just another Perl Hacker\n"]}; print ${${@}}[$#{@{${@}}}]
650 eval {die [[qq [Just another Perl Hacker]]]};; print
651 ${${${@}}[$#{@{${@}}}]}[$#{${@{${@}}}[$#{@{${@}}}]}]
652 SKIP: Abuses a fixed bug; what is in $#{...} must be an arrayref, not an array
654 ####### Closure returning itself.
655 $_ = "\nrekcaH lreP rehtona tsuJ"; my $chop; $chop = sub {print chop; $chop};
656 $chop -> () -> () -> () -> () -> () -> () -> () -> () -> () -> () -> () -> ()
657 -> () -> () -> () -> () -> () -> () -> () -> () -> () -> () -> () -> () -> ()
659 ####### Special blocks 1
660 BEGIN {print "Just " }
661 CHECK {print "another "}
662 INIT {print "Perl " }
663 END {print "Hacker\n"}
665 ####### Special blocks 2
666 END {print "Hacker\n"}
667 INIT {print "Perl " }
668 CHECK {print "another "}
669 BEGIN {print "Just " }
671 ####### Recursive regex.
672 my $qr = qr/^.+?(;).+?\1|;Just another Perl Hacker;|;.+$/;
676 ####### use lib 'coderef'
677 use lib sub {($\) = split /\./ => pop; print $"};
678 eval "use Just" || eval "use another" || eval "use Perl" || eval "use Hacker";
680 Just another Perl Hacker