1*256a93a4Safresh1#! ./perl 2*256a93a4Safresh1 3*256a93a4Safresh1# Check conversions of PV to NV/IV/UV 4*256a93a4Safresh1 5*256a93a4Safresh1BEGIN { 6*256a93a4Safresh1 chdir 't' if -d 't'; 7*256a93a4Safresh1 require './test.pl'; 8*256a93a4Safresh1 set_up_inc('../lib'); 9*256a93a4Safresh1 skip_all_without_dynamic_extension('Devel::Peek'); 10*256a93a4Safresh1} 11*256a93a4Safresh1 12*256a93a4Safresh1use strict; 13*256a93a4Safresh1use warnings; 14*256a93a4Safresh1use Devel::Peek; 15*256a93a4Safresh1use Config; 16*256a93a4Safresh1 17*256a93a4Safresh1# Use Devel::Peek::Dump in order to investigate SV flags for checking 18*256a93a4Safresh1# conversion behavior precisely. 19*256a93a4Safresh1# But unfortunately Devel::Peek::Dump always outputs to stderr, so 20*256a93a4Safresh1# a small wrapper to capture stderr into Perl string is implemented here 21*256a93a4Safresh1# to automate the test. 22*256a93a4Safresh1 23*256a93a4Safresh1package STDERRSaver { 24*256a93a4Safresh1 sub new { 25*256a93a4Safresh1 open my $old, '>&', *STDERR or die "Can't save STDERR: $!"; 26*256a93a4Safresh1 close STDERR; 27*256a93a4Safresh1 open STDERR, $_[1], $_[2] or die "Can't redirect STDERR: $!"; 28*256a93a4Safresh1 bless \$old, $_[0] || __PACKAGE__; 29*256a93a4Safresh1 } 30*256a93a4Safresh1 sub DESTROY { 31*256a93a4Safresh1 open STDERR, '>&', ${$_[0]} or die "Can't restore STDERR: $!"; 32*256a93a4Safresh1 close ${$_[0]}; 33*256a93a4Safresh1 } 34*256a93a4Safresh1} 35*256a93a4Safresh1 36*256a93a4Safresh1# These functions use &sub form to minimize argument manipulation. 37*256a93a4Safresh1 38*256a93a4Safresh1sub capture_dump 39*256a93a4Safresh1{ 40*256a93a4Safresh1 my $str; 41*256a93a4Safresh1 my @warnings; 42*256a93a4Safresh1 eval { 43*256a93a4Safresh1 local $SIG{__WARN__} = sub { push @warnings, $_[0] }; 44*256a93a4Safresh1 my $err = STDERRSaver->new('>', \$str); 45*256a93a4Safresh1 &Dump; 46*256a93a4Safresh1 !0; 47*256a93a4Safresh1 } or BAIL_OUT $@; # Avoid die() under test. 48*256a93a4Safresh1 note(@warnings) if @warnings; 49*256a93a4Safresh1 $str; 50*256a93a4Safresh1} 51*256a93a4Safresh1 52*256a93a4Safresh1# Implement Sv*OK in Perl. 53*256a93a4Safresh1 54*256a93a4Safresh1sub sv_flags 55*256a93a4Safresh1{ 56*256a93a4Safresh1 my $dump = &capture_dump; 57*256a93a4Safresh1 $dump =~ /^\h*FLAGS\h*=\h*\(\h*(.*?)\h*\)/m # be tolerant 58*256a93a4Safresh1 or note($dump), BAIL_OUT 'Cannot parse Devel::Peek::Dump output'; 59*256a93a4Safresh1 +{ map { $_ => !0 } split /\h*,\h*/, $1 }; 60*256a93a4Safresh1} 61*256a93a4Safresh1 62*256a93a4Safresh1sub SvUOK 63*256a93a4Safresh1{ 64*256a93a4Safresh1 my $flags = &sv_flags; 65*256a93a4Safresh1 $flags->{IOK} && $flags->{IsUV}; 66*256a93a4Safresh1} 67*256a93a4Safresh1 68*256a93a4Safresh1sub SvUOKp 69*256a93a4Safresh1{ 70*256a93a4Safresh1 my $flags = &sv_flags; 71*256a93a4Safresh1 $flags->{pIOK} && $flags->{IsUV}; 72*256a93a4Safresh1} 73*256a93a4Safresh1 74*256a93a4Safresh1sub SvIOKp_notIOK_notUV 75*256a93a4Safresh1{ 76*256a93a4Safresh1 my $flags = &sv_flags; 77*256a93a4Safresh1 $flags->{pIOK} && !$flags->{IOK} && !$flags->{IsUV}; 78*256a93a4Safresh1} 79*256a93a4Safresh1 80*256a93a4Safresh1sub SvIOK_notUV 81*256a93a4Safresh1{ 82*256a93a4Safresh1 my $flags = &sv_flags; 83*256a93a4Safresh1 $flags->{IOK} && !$flags->{IsUV}; 84*256a93a4Safresh1} 85*256a93a4Safresh1 86*256a93a4Safresh1sub SvNOK 87*256a93a4Safresh1{ 88*256a93a4Safresh1 (&sv_flags)->{NOK}; 89*256a93a4Safresh1} 90*256a93a4Safresh1 91*256a93a4Safresh1# This will be a quick test of Sv*OK* implemented here. 92*256a93a4Safresh1ok(SvIOK_notUV(2147483647), '2147483647 is not UV'); 93*256a93a4Safresh1 94*256a93a4Safresh1{ 95*256a93a4Safresh1 my $x = '12345.67'; 96*256a93a4Safresh1 my $y = $x; 97*256a93a4Safresh1 my $z = $y << 0; # "<<" requires UV operands 98*256a93a4Safresh1 is($z, 12345, "string '$x' to UV conversion"); 99*256a93a4Safresh1 ok(SvIOKp_notIOK_notUV($y), 'string to UV conversion caches IV'); 100*256a93a4Safresh1 is($y >> 0, 12345, 'reusing cached IV'); 101*256a93a4Safresh1} 102*256a93a4Safresh1 103*256a93a4Safresh1{ 104*256a93a4Safresh1 my $x = '40e+8'; 105*256a93a4Safresh1 my $y = $x; 106*256a93a4Safresh1 my $z = $y | 0; # "|" also requires UV operands 107*256a93a4Safresh1 is($z, 4000000000, "string '$x' to UV conversion"); 108*256a93a4Safresh1 ok(SvNOK($y), "string to UV conversion caches NV"); 109*256a93a4Safresh1 ok(SvUOK(4000000000) ? SvUOK($y) : SvIOK_notUV($y), 110*256a93a4Safresh1 'string to UV conversion caches IV or UV'); 111*256a93a4Safresh1 is($y ^ 0, 4000000000, 'reusing cached IV or UV'); 112*256a93a4Safresh1} 113*256a93a4Safresh1 114*256a93a4Safresh1my $uv_max = ~0; 115*256a93a4Safresh1 116*256a93a4Safresh1{ 117*256a93a4Safresh1 my $x = $uv_max * 7; # Some large value not representable in IV/UV 118*256a93a4Safresh1 my $y = "$x"; # Convert to string 119*256a93a4Safresh1 my $z = $y << 0; 120*256a93a4Safresh1 is($z, $uv_max, 'large value in string is coerced to UV_MAX when UV is requested'); 121*256a93a4Safresh1 ok(SvUOKp($y), 'converted UV is cached'); 122*256a93a4Safresh1 is($y >> 0, $uv_max, 'reusing cached UV_MAX'); 123*256a93a4Safresh1 my $v = $x << 0; # Now NV to UV conversion 124*256a93a4Safresh1 is($v, $uv_max, 'large NV is coerced to UV_MAX when UV is requested'); 125*256a93a4Safresh1 ok(SvUOKp($v), 'converted UV is cached'); 126*256a93a4Safresh1 is($x >> 0, $uv_max, 'reusing cached UV_MAX'); 127*256a93a4Safresh1} 128*256a93a4Safresh1 129*256a93a4Safresh1done_testing(); 130