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 157     157   998 use strict;
  157         321  
  157         4245  
3 157     157   762 use warnings;
  157         383  
  157         6169  
4              
5             our $VERSION = '0.000153';
6              
7 157     157   853 use Carp qw/croak/;
  157         293  
  157         6630  
8 157     157   1004 use B;
  157         326  
  157         10250  
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 157     157   1129 use base 'Exporter';
  157         398  
  157         37281  
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 93 sub slot_to_sig { $SLOTMAP{$_[0]} || croak "unsupported slot: '$_[0]'" }
30 4 50   4 1 58 sub sig_to_slot { $SIGMAP{$_[0]} || croak "unsupported sigil: $_[0]" }
31              
32             sub get_stash {
33 1054   33 1054 1 2016 my $package = shift || caller;
34 157     157   1155 no strict 'refs';
  157         423  
  157         14778  
35 1054         1315 return \%{"${package}\::"};
  1054         2971  
36             }
37              
38             sub get_glob {
39 1059     1059 1 2080 my $sym = _parse_symbol(scalar(caller), @_);
40 157     157   1072 no strict 'refs';
  157         371  
  157         6135  
41 157     157   962 no warnings 'once';
  157         373  
  157         131137  
42 1059         1492 return \*{"$sym->{package}\::$sym->{name}"};
  1059         3027  
43             }
44              
45 58     58 1 205 sub parse_symbol { _parse_symbol(scalar(caller), @_) }
46              
47             sub _parse_symbol {
48 2149     2149   3651 my ($caller, $symbol, $package) = @_;
49              
50 2149 100       3966 if (ref($symbol)) {
51 1499         2145 my $pkg = $symbol->{package};
52              
53 1499 100 100     3932 croak "Symbol package ($pkg) and package argument ($package) do not match"
      66        
54             if $pkg && $package && $pkg ne $package;
55              
56 1498   66     3255 $symbol->{package} ||= $caller;
57              
58 1498         2751 return $symbol;
59             }
60              
61 650 50       1244 utf8::downgrade($symbol) if $] == 5.010000; # prevent crash on 5.10.0
62 650 50       4112 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       1496 $pkg = $pkg
    100          
67             ? $pkg eq '::'
68             ? 'main'
69             : substr($pkg, 0, -2)
70             : undef;
71              
72 650 100 100     1567 croak "Symbol package ($pkg) and package argument ($package) do not match"
      66        
73             if $pkg && $package && $pkg ne $package;
74              
75 649   100     1846 $sig ||= '&';
76 649   33     1676 my $type = $SIGMAP{$sig} || croak "unsupported sigil: '$sig'";
77              
78 649   66     1511 my $real_package = $package || $pkg || $caller;
79              
80             return {
81 649         3289 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 2150 my $sym = _parse_symbol(scalar(caller), @_);
91              
92 944         1656 my $name = $sym->{name};
93 944         1247 my $type = $sym->{type};
94 944         1278 my $package = $sym->{package};
95 944         1343 my $symbol = $sym->{symbol};
96              
97 944         1590 my $stash = get_stash($package);
98 944 100       2215 return undef unless exists $stash->{$name};
99              
100 882         1396 my $glob = get_glob($sym);
101 882 100 100     1938 return *{$glob}{$type} if $type ne 'SCALAR' && defined(*{$glob}{$type});
  398         1695  
  787         2177  
102              
103 484 50       850 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 484         1484 my $sv = B::svref_2object($glob)->SV;
118 484 100       1643 return *{$glob}{$type} if $sv->isa('B::SV');
  104         282  
119 380 50       800 return undef unless $sv->isa('B::SPECIAL');
120 380 50       729 return *{$glob}{$type} if $B::specialsv_name[$$sv] ne 'Nullsv';
  0         0  
121 380         746 return undef;
122             }
123              
124             sub purge_symbol {
125 88     88 1 384 my $sym = _parse_symbol(scalar(caller), @_);
126              
127 88         149 local *GLOBCLONE = *{get_glob($sym)};
  88         162  
128 88         217 delete get_stash($sym->{package})->{$sym->{name}};
129 88         174 my $new_glob = get_glob($sym);
130              
131 88         223 for my $type (qw/CODE SCALAR HASH ARRAY FORMAT IO/) {
132 528 100       980 next if $type eq $sym->{type};
133 440         1190 my $ref = get_symbol({type => $type, name => 'GLOBCLONE', sigil => $SLOTMAP{$type}}, __PACKAGE__);
134 440 100       1124 next unless $ref;
135 21         44 *$new_glob = $ref;
136             }
137              
138 88         616 return *GLOBCLONE{$sym->{type}};
139             }
140              
141             1;
142              
143             __END__