Deparse-core.t revision 1.5
1#!./perl 2 3# Test the core keywords. 4# 5# Initially this test file just checked that CORE::foo got correctly 6# deparsed as CORE::foo, hence the name. It's since been expanded 7# to fully test both CORE:: versus none, plus that any arguments 8# are correctly deparsed. It also cross-checks against regen/keywords.pl 9# to make sure we've tested all keywords, and with the correct strength. 10# 11# A keyword can be either weak or strong. Strong keywords can never be 12# overridden, while weak ones can. So deparsing of weak keywords depends 13# on whether a sub of that name has been created: 14# 15# for both: keyword(..) deparsed as keyword(..) 16# for weak: CORE::keyword(..) deparsed as CORE::keyword(..) 17# for strong: CORE::keyword(..) deparsed as keyword(..) 18# 19# Three permutations of lex/nonlex args are checked for: 20# 21# foo($a,$b,$c,...) 22# foo(my $a,$b,$c,...) 23# my ($a,$b,$c,...); foo($a,$b,$c,...) 24# 25# Note that tests for prefixing feature.pm-enabled keywords with CORE:: when 26# feature.pm is not enabled are in deparse.t, as they fit that format better. 27 28 29BEGIN { 30 require Config; 31 if (($Config::Config{extensions} !~ /\bB\b/) ){ 32 print "1..0 # Skip -- Perl configured without B module\n"; 33 exit 0; 34 } 35} 36 37use strict; 38use Test::More; 39 40use feature (sprintf(":%vd", $^V)); # to avoid relying on the feature 41 # logic to add CORE:: 42use B::Deparse; 43my $deparse = B::Deparse->new(); 44 45my %SEEN; 46my %SEEN_STRENGTH; 47 48# For a given keyword, create a sub of that name, 49# then deparse 3 different assignment expressions 50# using that keyword. See if the $expr we get back 51# matches $expected_expr. 52 53sub testit { 54 my ($keyword, $expr, $expected_expr, $lexsub) = @_; 55 56 $expected_expr //= $expr; 57 $SEEN{$keyword} = 1; 58 59 # lex=0: () = foo($a,$b,$c) 60 # lex=1: my ($a,$b); () = foo($a,$b,$c) 61 # lex=2: () = foo(my $a,$b,$c) 62 for my $lex (0, 1, 2) { 63 next if ($lex and $keyword =~ /local|our|state|my/); 64 my $vars = $lex == 1 ? 'my($a, $b, $c, $d, $e);' . "\n " : ""; 65 66 if ($lex == 2) { 67 my $repl = 'my $a'; 68 if ($expr =~ 'CORE::do') { 69 # do foo() is a syntax error, so B::Deparse emits 70 # do (foo()), but does not distinguish between foo and my, 71 # because it is too complicated. 72 $repl = '(my $a)'; 73 } 74 s/\$a/$repl/ for $expr, $expected_expr; 75 } 76 77 my $desc = "$keyword: lex=$lex $expr => $expected_expr"; 78 $desc .= " (lex sub)" if $lexsub; 79 80 my $code; 81 my $code_ref; 82 if ($lexsub) { 83 package lexsubtest; 84 no warnings 'experimental::lexical_subs'; 85 use feature 'lexical_subs'; 86 no strict 'vars'; 87 $code = "sub { state sub $keyword; ${vars}() = $expr }"; 88 $code = "use feature 'isa';\n$code" if $keyword eq "isa"; 89 $code = "use feature 'switch';\n$code" if $keyword eq "break"; 90 $code_ref = eval $code or die "$@ in $expr"; 91 } 92 else { 93 package test; 94 use subs (); 95 import subs $keyword; 96 $code = "no strict 'vars'; sub { ${vars}() = $expr }"; 97 $code = "use feature 'isa';\n$code" if $keyword eq "isa"; 98 $code = "use feature 'switch';\n$code" if $keyword eq "break"; 99 $code_ref = eval $code or die "$@ in $expr"; 100 } 101 102 my $got_text = $deparse->coderef2text($code_ref); 103 104 unless ($got_text =~ / 105 package (?:lexsub)?test; 106(?: BEGIN \{\$\{\^WARNING_BITS\} = "[^"]+"\} 107)? use strict 'refs', 'subs'; 108 use feature [^\n]+ 109(?: (?:CORE::)?state sub \w+; 110)? \Q$vars\E\(\) = (.*) 111\}/s) { 112 ::fail($desc); 113 ::diag("couldn't extract line from boilerplate\n"); 114 ::diag($got_text); 115 return; 116 } 117 118 my $got_expr = $1; 119 is $got_expr, $expected_expr, $desc 120 or ::diag("ORIGINAL CODE:\n$code");; 121 } 122} 123 124 125# Deparse can't distinguish 'and' from '&&' etc 126my %infix_map = qw(and && or ||); 127 128# Test a keyword that is a binary infix operator, like 'cmp'. 129# $parens - "$a op $b" is deparsed as "($a op $b)" 130# $strong - keyword is strong 131 132sub do_infix_keyword { 133 my ($keyword, $parens, $strong) = @_; 134 $SEEN_STRENGTH{$keyword} = $strong; 135 my $expr = "(\$a $keyword \$b)"; 136 my $nkey = $infix_map{$keyword} // $keyword; 137 my $expr = "(\$a $keyword \$b)"; 138 my $exp = "\$a $nkey \$b"; 139 $exp = "($exp)" if $parens; 140 $exp .= ";"; 141 # with infix notation, a keyword is always interpreted as core, 142 # so no need for Deparse to disambiguate with CORE:: 143 testit $keyword, "(\$a CORE::$keyword \$b)", $exp; 144 testit $keyword, "(\$a $keyword \$b)", $exp; 145 testit $keyword, "(\$a CORE::$keyword \$b)", $exp, 1; 146 testit $keyword, "(\$a $keyword \$b)", $exp, 1; 147 if (!$strong) { 148 # B::Deparse fully qualifies any sub whose name is a keyword, 149 # imported or not, since the importedness may not be reproduced by 150 # the deparsed code. x is special. 151 my $pre = "test::" x ($keyword ne 'x'); 152 testit $keyword, "$keyword(\$a, \$b)", "$pre$keyword(\$a, \$b);"; 153 } 154 testit $keyword, "$keyword(\$a, \$b)", "$keyword(\$a, \$b);", 1; 155} 156 157# Test a keyword that is a standard op/function, like 'index(...)'. 158# $narg - how many args to test it with 159# $parens - "foo $a, $b" is deparsed as "foo($a, $b)" 160# $dollar - an extra '$_' arg will appear in the deparsed output 161# $strong - keyword is strong 162 163 164sub do_std_keyword { 165 my ($keyword, $narg, $parens, $dollar, $strong) = @_; 166 167 $SEEN_STRENGTH{$keyword} = $strong; 168 169 for my $core (0,1) { # if true, add CORE:: to keyword being deparsed 170 for my $lexsub (0,1) { # if true, define lex sub 171 my @code; 172 for my $do_exp(0, 1) { # first create expr, then expected-expr 173 my @args = map "\$$_", (undef,"a".."z")[1..$narg]; 174 push @args, '$_' 175 if $dollar && $do_exp && ($strong && !$lexsub or $core); 176 my $args = join(', ', @args); 177 # XXX $lex_parens is temporary, until lex subs are 178 # deparsed properly. 179 my $lex_parens = 180 !$core && $do_exp && $lexsub && $keyword ne 'map'; 181 $args = ((!$core && !$strong) || $parens || $lex_parens) 182 ? "($args)" 183 : @args 184 ? " $args" 185 : ""; 186 push @code, ( 187 ($core && !($do_exp && $strong)) 188 ? "CORE::" 189 : $lexsub && $do_exp 190 ? "CORE::" x $core 191 : $do_exp && !$core && !$strong 192 ? "test::" 193 : "" 194 ) . "$keyword$args;"; 195 } 196 # code[0]: to run; code[1]: expected 197 testit $keyword, @code, $lexsub; 198 } 199 } 200} 201 202 203while (<DATA>) { 204 chomp; 205 s/#.*//; 206 next unless /\S/; 207 208 my @fields = split; 209 die "not 3 fields" unless @fields == 3; 210 my ($keyword, $args, $flags) = @fields; 211 212 $args = '012' if $args eq '@'; 213 214 my $parens = $flags =~ s/p//; 215 my $invert1 = $flags =~ s/1//; 216 my $dollar = $flags =~ s/\$//; 217 my $strong = $flags =~ s/\+//; 218 die "unrecognised flag(s): '$flags'" unless $flags =~ /^-?$/; 219 220 if ($args eq 'B') { # binary infix 221 die "$keyword: binary (B) op can't have '\$' flag\\n" if $dollar; 222 die "$keyword: binary (B) op can't have '1' flag\\n" if $invert1; 223 do_infix_keyword($keyword, $parens, $strong); 224 } 225 else { 226 my @narg = split //, $args; 227 for my $n (0..$#narg) { 228 my $narg = $narg[$n]; 229 my $p = $parens; 230 $p = !$p if ($n == 0 && $invert1); 231 do_std_keyword($keyword, $narg, $p, (!$n && $dollar), $strong); 232 } 233 } 234} 235 236 237# Special cases 238 239testit dbmopen => 'CORE::dbmopen(%foo, $bar, $baz);'; 240testit dbmclose => 'CORE::dbmclose %foo;'; 241 242testit delete => 'CORE::delete $h{\'foo\'};', 'delete $h{\'foo\'};'; 243testit delete => 'CORE::delete $h{\'foo\'};', undef, 1; 244testit delete => 'CORE::delete @h{\'foo\'};', undef, 1; 245testit delete => 'CORE::delete $h[0];', undef, 1; 246testit delete => 'CORE::delete @h[0];', undef, 1; 247testit delete => 'delete $h{\'foo\'};', 'delete $h{\'foo\'};'; 248 249# do is listed as strong, but only do { block } is strong; 250# do $file is weak, so test it separately here 251testit do => 'CORE::do $a;'; 252testit do => 'do $a;', 'test::do($a);'; 253testit do => 'CORE::do { 1 }', 254 "do {\n 1\n };"; 255testit do => 'CORE::do { 1 }', 256 "CORE::do {\n 1\n };", 1; 257testit do => 'do { 1 };', 258 "do {\n 1\n };"; 259 260testit each => 'CORE::each %bar;'; 261testit each => 'CORE::each @foo;'; 262 263testit eof => 'CORE::eof();'; 264 265testit exists => 'CORE::exists $h{\'foo\'};', 'exists $h{\'foo\'};'; 266testit exists => 'CORE::exists $h{\'foo\'};', undef, 1; 267testit exists => 'CORE::exists &foo;', undef, 1; 268testit exists => 'CORE::exists $h[0];', undef, 1; 269testit exists => 'exists $h{\'foo\'};', 'exists $h{\'foo\'};'; 270 271testit exec => 'CORE::exec($foo $bar);'; 272 273testit glob => 'glob;', 'glob($_);'; 274testit glob => 'CORE::glob;', 'CORE::glob($_);'; 275testit glob => 'glob $a;', 'glob($a);'; 276testit glob => 'CORE::glob $a;', 'CORE::glob($a);'; 277 278testit grep => 'CORE::grep { $a } $b, $c', 'grep({$a;} $b, $c);'; 279 280testit keys => 'CORE::keys %bar;'; 281testit keys => 'CORE::keys @bar;'; 282 283testit map => 'CORE::map { $a } $b, $c', 'map({$a;} $b, $c);'; 284 285testit not => '3 unless CORE::not $a && $b;'; 286 287testit pop => 'CORE::pop @foo;'; 288 289testit push => 'CORE::push @foo;', 'CORE::push(@foo);'; 290testit push => 'CORE::push @foo, 1;', 'CORE::push(@foo, 1);'; 291testit push => 'CORE::push @foo, 1, 2;', 'CORE::push(@foo, 1, 2);'; 292 293testit readline => 'CORE::readline $a . $b;'; 294 295testit readpipe => 'CORE::readpipe $a + $b;'; 296 297testit reverse => 'CORE::reverse sort(@foo);'; 298 299testit shift => 'CORE::shift @foo;'; 300 301testit splice => q{CORE::splice @foo;}, q{CORE::splice(@foo);}; 302testit splice => q{CORE::splice @foo, 0;}, q{CORE::splice(@foo, 0);}; 303testit splice => q{CORE::splice @foo, 0, 1;}, q{CORE::splice(@foo, 0, 1);}; 304testit splice => q{CORE::splice @foo, 0, 1, 'a';}, q{CORE::splice(@foo, 0, 1, 'a');}; 305testit splice => q{CORE::splice @foo, 0, 1, 'a', 'b';}, q{CORE::splice(@foo, 0, 1, 'a', 'b');}; 306 307# note that the test does '() = split...' which is why the 308# limit is optimised to 1 309testit split => 'split;', q{split(' ', $_, 1);}; 310testit split => 'CORE::split;', q{split(' ', $_, 1);}; 311testit split => 'split $a;', q{split(/$a/u, $_, 1);}; 312testit split => 'CORE::split $a;', q{split(/$a/u, $_, 1);}; 313testit split => 'split $a, $b;', q{split(/$a/u, $b, 1);}; 314testit split => 'CORE::split $a, $b;', q{split(/$a/u, $b, 1);}; 315testit split => 'split $a, $b, $c;', q{split(/$a/u, $b, $c);}; 316testit split => 'CORE::split $a, $b, $c;', q{split(/$a/u, $b, $c);}; 317 318testit sub => 'CORE::sub { $a, $b }', 319 "sub {\n \$a, \$b;\n }\n ;"; 320 321testit system => 'CORE::system($foo $bar);'; 322 323testit unshift => 'CORE::unshift @foo;', 'CORE::unshift(@foo);'; 324testit unshift => 'CORE::unshift @foo, 1;', 'CORE::unshift(@foo, 1);'; 325testit unshift => 'CORE::unshift @foo, 1, 2;', 'CORE::unshift(@foo, 1, 2);'; 326 327testit values => 'CORE::values %bar;'; 328testit values => 'CORE::values @foo;'; 329 330 331# XXX These are deparsed wrapped in parens. 332# whether they should be, I don't know! 333 334testit dump => '(CORE::dump);'; 335testit dump => '(CORE::dump FOO);'; 336testit goto => '(CORE::goto);', '(goto);'; 337testit goto => '(CORE::goto FOO);', '(goto FOO);'; 338testit last => '(CORE::last);', '(last);'; 339testit last => '(CORE::last FOO);', '(last FOO);'; 340testit next => '(CORE::next);', '(next);'; 341testit next => '(CORE::next FOO);', '(next FOO);'; 342testit redo => '(CORE::redo);', '(redo);'; 343testit redo => '(CORE::redo FOO);', '(redo FOO);'; 344testit redo => '(CORE::redo);', '(redo);'; 345testit redo => '(CORE::redo FOO);', '(redo FOO);'; 346testit return => '(return);', '(return);'; 347testit return => '(CORE::return);', '(return);'; 348 349# these are the keywords I couldn't think how to test within this framework 350 351my %not_tested = map { $_ => 1} qw( 352 __DATA__ 353 __END__ 354 __FILE__ 355 __LINE__ 356 __PACKAGE__ 357 AUTOLOAD 358 BEGIN 359 CHECK 360 CORE 361 DESTROY 362 END 363 INIT 364 UNITCHECK 365 catch 366 default 367 defer 368 else 369 elsif 370 finally 371 for 372 foreach 373 format 374 given 375 if 376 m 377 no 378 package 379 q 380 qq 381 qr 382 qw 383 qx 384 require 385 s 386 tr 387 try 388 unless 389 until 390 use 391 when 392 while 393 y 394); 395 396# Sanity check against keyword data: 397# make sure we haven't missed any keywords, 398# and that we got the strength right. 399 400SKIP: 401{ 402 skip "sanity checks when not PERL_CORE", 1 unless defined $ENV{PERL_CORE}; 403 my $count = 0; 404 my $file = '../regen/keywords.pl'; 405 my $pass = 1; 406 if (open my $fh, '<', $file) { 407 while (<$fh>) { 408 last if /^__END__$/; 409 } 410 while (<$fh>) { 411 next unless /^([+\-])(\w+)$/; 412 my ($strength, $key) = ($1, $2); 413 $strength = ($strength eq '+') ? 1 : 0; 414 $count++; 415 if (!$SEEN{$key} && !$not_tested{$key}) { 416 diag("keyword '$key' seen in $file, but not tested here!!"); 417 $pass = 0; 418 } 419 if (exists $SEEN_STRENGTH{$key} and $SEEN_STRENGTH{$key} != $strength) { 420 diag("keyword '$key' strengh as seen in $file doen't match here!!"); 421 $pass = 0; 422 } 423 } 424 } 425 else { 426 diag("Can't open $file: $!"); 427 $pass = 0; 428 } 429 # insanity check 430 if ($count < 200) { 431 diag("Saw $count keywords: less than 200!"); 432 $pass = 0; 433 } 434 ok($pass, "sanity checks"); 435} 436 437done_testing(); 438 439__DATA__ 440# 441# format: 442# keyword args flags 443# 444# args consists of: 445# * one of more digits indictating which lengths of args the function accepts, 446# * or 'B' to indiate a binary infix operator, 447# * or '@' to indicate a list function. 448# 449# Flags consists of the following (or '-' if no flags): 450# + : strong keyword: can't be overrriden 451# p : the args are parenthesised on deparsing; 452# 1 : parenthesising of 1st arg length is inverted 453# so '234 p1' means: foo a1,a2; foo(a1,a2,a3); foo(a1,a2,a3,a4) 454# $ : on the first argument length, there is an implicit extra 455# '$_' arg which will appear on deparsing; 456# e.g. 12p$ will be tested as: foo(a1); foo(a1,a2); 457# and deparsed as: foo(a1, $_); foo(a1,a2); 458# 459# XXX Note that we really should get this data from regen/keywords.pl 460# and regen/opcodes (augmented if necessary), rather than duplicating it 461# here. 462 463__SUB__ 0 - 464abs 01 $ 465accept 2 p 466alarm 01 $ 467and B - 468atan2 2 p 469bind 2 p 470binmode 12 p 471bless 1 p 472break 0 - 473caller 0 - 474chdir 01 - 475chmod @ p1 476chomp @ $ 477chop @ $ 478chown @ p1 479chr 01 $ 480chroot 01 $ 481close 01 - 482closedir 1 - 483cmp B - 484connect 2 p 485continue 0 - 486cos 01 $ 487crypt 2 p 488# dbmopen handled specially 489# dbmclose handled specially 490defined 01 $+ 491# delete handled specially 492die @ p1 493# do handled specially 494# dump handled specially 495# each handled specially 496endgrent 0 - 497endhostent 0 - 498endnetent 0 - 499endprotoent 0 - 500endpwent 0 - 501endservent 0 - 502eof 01 - # also tested specially 503eq B - 504eval 01 $+ 505evalbytes 01 $ 506exec @ p1 # also tested specially 507# exists handled specially 508exit 01 - 509exp 01 $ 510fc 01 $ 511fcntl 3 p 512fileno 1 - 513flock 2 p 514fork 0 - 515formline 2 p 516ge B - 517getc 01 - 518getgrent 0 - 519getgrgid 1 - 520getgrnam 1 - 521gethostbyaddr 2 p 522gethostbyname 1 - 523gethostent 0 - 524getlogin 0 - 525getnetbyaddr 2 p 526getnetbyname 1 - 527getnetent 0 - 528getpeername 1 - 529getpgrp 1 - 530getppid 0 - 531getpriority 2 p 532getprotobyname 1 - 533getprotobynumber 1 p 534getprotoent 0 - 535getpwent 0 - 536getpwnam 1 - 537getpwuid 1 - 538getservbyname 2 p 539getservbyport 2 p 540getservent 0 - 541getsockname 1 - 542getsockopt 3 p 543# given handled specially 544grep 123 p+ # also tested specially 545# glob handled specially 546# goto handled specially 547gmtime 01 - 548gt B - 549hex 01 $ 550index 23 p 551int 01 $ 552ioctl 3 p 553isa B - 554join 13 p 555# keys handled specially 556kill 123 p 557# last handled specially 558lc 01 $ 559lcfirst 01 $ 560le B - 561length 01 $ 562link 2 p 563listen 2 p 564local 1 p+ 565localtime 01 - 566lock 1 - 567log 01 $ 568lstat 01 $ 569lt B - 570map 123 p+ # also tested specially 571mkdir @ p$ 572msgctl 3 p 573msgget 2 p 574msgrcv 5 p 575msgsnd 3 p 576my 123 p+ # skip with 0 args, as my() => () 577ne B - 578# next handled specially 579# not handled specially 580oct 01 $ 581open 12345 p 582opendir 2 p 583or B - 584ord 01 $ 585our 123 p+ # skip with 0 args, as our() => () 586pack 123 p 587pipe 2 p 588pop 0 1 # also tested specially 589pos 01 $+ 590print @ p$+ 591printf @ p$+ 592prototype 1 + 593# push handled specially 594quotemeta 01 $ 595rand 01 - 596read 34 p 597readdir 1 - 598# readline handled specially 599readlink 01 $ 600# readpipe handled specially 601recv 4 p 602# redo handled specially 603ref 01 $ 604rename 2 p 605# XXX This code prints 'Undefined subroutine &main::require called': 606# use subs (); import subs 'require'; 607# eval q[no strict 'vars'; sub { () = require; }]; print $@; 608# so disable for now 609#require 01 $+ 610reset 01 - 611# return handled specially 612reverse @ p1 # also tested specially 613rewinddir 1 - 614rindex 23 p 615rmdir 01 $ 616say @ p$+ 617scalar 1 + 618seek 3 p 619seekdir 2 p 620select 014 p1 621semctl 4 p 622semget 3 p 623semop 2 p 624send 34 p 625setgrent 0 - 626sethostent 1 - 627setnetent 1 - 628setpgrp 2 p 629setpriority 3 p 630setprotoent 1 - 631setpwent 0 - 632setservent 1 - 633setsockopt 4 p 634shift 0 1 # also tested specially 635shmctl 3 p 636shmget 3 p 637shmread 4 p 638shmwrite 4 p 639shutdown 2 p 640sin 01 $ 641sleep 01 - 642socket 4 p 643socketpair 5 p 644sort 12 p+ 645# split handled specially 646# splice handled specially 647sprintf 123 p 648sqrt 01 $ 649srand 01 - 650stat 01 $ 651state 123 p1+ # skip with 0 args, as state() => () 652study 01 $+ 653# sub handled specially 654substr 234 p 655symlink 2 p 656syscall 2 p 657sysopen 34 p 658sysread 34 p 659sysseek 3 p 660system @ p1 # also tested specially 661syswrite 234 p 662tell 01 - 663telldir 1 - 664tie 234 p 665tied 1 - 666time 0 - 667times 0 - 668truncate 2 p 669uc 01 $ 670ucfirst 01 $ 671umask 01 - 672undef 01 + 673unlink @ p$ 674unpack 12 p$ 675# unshift handled specially 676untie 1 - 677utime @ p1 678# values handled specially 679vec 3 p 680wait 0 - 681waitpid 2 p 682wantarray 0 - 683warn @ p1 684write 01 - 685x B - 686xor B p 687