File Coverage

blib/lib/Test2/Util/Stash.pm
Criterion Covered Total %
statement 76 92 82.6
branch 27 42 64.2
condition 21 29 72.4
subroutine 16 17 94.1
pod 7 7 100.0
total 147 187 78.6


line stmt bran cond sub pod time code
1             package Test2::Util::Stash;
2 158     158   1201 use strict;
  158         354  
  158         4690  
3 158     158   774 use warnings;
  158         318  
  158         6510  
4              
5             our $VERSION = '0.000156';
6              
7 158     158   932 use Carp qw/croak/;
  158         307  
  158         6721  
8 158     158   992 use B;
  158         565  
  158         12110  
9              
10             our @EXPORT_OK = qw{
11             get_stash
12             get_glob
13             get_symbol
14             parse_symbol
15             purge_symbol
16             slot_to_sig sig_to_slot
17             };
18 158     158   1186 use base 'Exporter';
  158         447  
  158         41682  
19              
20             my %SIGMAP = (
21             '&' => 'CODE',
22             '$' => 'SCALAR',
23             '%' => 'HASH',
24             '@' => 'ARRAY',
25             );
26              
27             my %SLOTMAP = reverse %SIGMAP;
28              
29 19 50   19 1 102 sub slot_to_sig { $SLOTMAP{$_[0]} || croak "unsupported slot: '$_[0]'" }
30 4 50   4 1 30 sub sig_to_slot { $SIGMAP{$_[0]} || croak "unsupported sigil: $_[0]" }
31              
32             sub get_stash {
33 1054   33 1054 1 2036 my $package = shift || caller;
34 158     158   1317 no strict 'refs';
  158         488  
  158         18149  
35 1054         1398 return \%{"${package}\::"};
  1054         3229  
36             }
37              
38             sub get_glob {
39 1059     1059 1 2196 my $sym = _parse_symbol(scalar(caller), @_);
40 158     158   1133 no strict 'refs';
  158         352  
  158         5761  
41 158     158   1036 no warnings 'once';
  158         454  
  158         148148  
42 1059         1575 return \*{"$sym->{package}\::$sym->{name}"};
  1059         3420  
43             }
44              
45 58     58 1 198 sub parse_symbol { _parse_symbol(scalar(caller), @_) }
46              
47             sub _parse_symbol {
48 2149     2149   3899 my ($caller, $symbol, $package) = @_;
49              
50 2149 100       4174 if (ref($symbol)) {
51 1499         2238 my $pkg = $symbol->{package};
52              
53 1499 100 100     4332 croak "Symbol package ($pkg) and package argument ($package) do not match"
      66        
54             if $pkg && $package && $pkg ne $package;
55              
56 1498   66     3487 $symbol->{package} ||= $caller;
57              
58 1498         2869 return $symbol;
59             }
60              
61 650 50       1408 utf8::downgrade($symbol) if $] == 5.010000; # prevent crash on 5.10.0
62 650 50       4446 my ($sig, $pkg, $name) = ($symbol =~ m/^(\W?)(.*::)?([^:]+)$/)
63             or croak "Invalid symbol: '$symbol'";
64              
65             # Normalize package, '::' becomes 'main', 'Foo::' becomes 'Foo'
66 650 100       1723 $pkg = $pkg
    100          
67             ? $pkg eq '::'
68             ? 'main'
69             : substr($pkg, 0, -2)
70             : undef;
71              
72 650 100 100     1766 croak "Symbol package ($pkg) and package argument ($package) do not match"
      66        
73             if $pkg && $package && $pkg ne $package;
74              
75 649   100     2013 $sig ||= '&';
76 649   33     1664 my $type = $SIGMAP{$sig} || croak "unsupported sigil: '$sig'";
77              
78 649   66     1521 my $real_package = $package || $pkg || $caller;
79              
80             return {
81 649         5268 name => $name,
82             sigil => $sig,
83             type => $type,
84             symbol => "${sig}${real_package}::${name}",
85             package => $real_package,
86             };
87             }
88              
89             sub get_symbol {
90 944     944 1 2200 my $sym = _parse_symbol(scalar(caller), @_);
91              
92 944         1750 my $name = $sym->{name};
93 944         1403 my $type = $sym->{type};
94 944         1346 my $package = $sym->{package};
95 944         1339 my $symbol = $sym->{symbol};
96              
97 944         1699 my $stash = get_stash($package);
98 944 100       2731 return undef unless exists $stash->{$name};
99              
100 882         1486 my $glob = get_glob($sym);
101 882 100 100     2061 return *{$glob}{$type} if $type ne 'SCALAR' && defined(*{$glob}{$type});
  399         1853  
  787         2413  
102              
103 483 50       889 if ($] < 5.010) {
104 0 0       0 return undef unless defined(*{$glob}{$type});
  0         0  
105              
106             {
107 0         0 local ($@, $!);
  0         0  
108 0     0   0 local $SIG{__WARN__} = sub { 1 };
  0         0  
109 0 0       0 return *{$glob}{$type} if eval "package $package; my \$y = $symbol; 1";
  0         0  
110             }
111              
112 0 0       0 return undef unless defined *{$glob}{$type};
  0         0  
113 0 0       0 return *{$glob}{$type} if defined ${*{$glob}{$type}};
  0         0  
  0         0  
  0         0  
114 0         0 return undef;
115             }
116              
117 483         1566 my $sv = B::svref_2object($glob)->SV;
118 483 100       1699 return *{$glob}{$type} if $sv->isa('B::SV');
  99         288  
119 384 50       875 return undef unless $sv->isa('B::SPECIAL');
120 384 50       797 return *{$glob}{$type} if $B::specialsv_name[$$sv] ne 'Nullsv';
  0         0  
121 384         768 return undef;
122             }
123              
124             sub purge_symbol {
125 88     88 1 452 my $sym = _parse_symbol(scalar(caller), @_);
126              
127 88         159 local *GLOBCLONE = *{get_glob($sym)};
  88         186  
128 88         239 delete get_stash($sym->{package})->{$sym->{name}};
129 88         216 my $new_glob = get_glob($sym);
130              
131 88         234 for my $type (qw/CODE SCALAR HASH ARRAY FORMAT IO/) {
132 528 100       1070 next if $type eq $sym->{type};
133 440         1288 my $ref = get_symbol({type => $type, name => 'GLOBCLONE', sigil => $SLOTMAP{$type}}, __PACKAGE__);
134 440 100       1199 next unless $ref;
135 21         44 *$new_glob = $ref;
136             }
137              
138 88         732 return *GLOBCLONE{$sym->{type}};
139             }
140              
141             1;
142              
143             __END__