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   1045 use strict;
  158         338  
  158         4488  
3 158     158   955 use warnings;
  158         330  
  158         6564  
4              
5             our $VERSION = '0.000155';
6              
7 158     158   1226 use Carp qw/croak/;
  158         336  
  158         7365  
8 158     158   991 use B;
  158         369  
  158         11288  
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   1180 use base 'Exporter';
  158         421  
  158         41377  
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 101 sub slot_to_sig { $SLOTMAP{$_[0]} || croak "unsupported slot: '$_[0]'" }
30 4 50   4 1 29 sub sig_to_slot { $SIGMAP{$_[0]} || croak "unsupported sigil: $_[0]" }
31              
32             sub get_stash {
33 1046   33 1046 1 2141 my $package = shift || caller;
34 158     158   1263 no strict 'refs';
  158         424  
  158         16084  
35 1046         1470 return \%{"${package}\::"};
  1046         3351  
36             }
37              
38             sub get_glob {
39 1051     1051 1 2196 my $sym = _parse_symbol(scalar(caller), @_);
40 158     158   1190 no strict 'refs';
  158         446  
  158         6413  
41 158     158   1013 no warnings 'once';
  158         505  
  158         148998  
42 1051         1568 return \*{"$sym->{package}\::$sym->{name}"};
  1051         3378  
43             }
44              
45 50     50 1 179 sub parse_symbol { _parse_symbol(scalar(caller), @_) }
46              
47             sub _parse_symbol {
48 2125     2125   4015 my ($caller, $symbol, $package) = @_;
49              
50 2125 100       4259 if (ref($symbol)) {
51 1491         2237 my $pkg = $symbol->{package};
52              
53 1491 100 100     4442 croak "Symbol package ($pkg) and package argument ($package) do not match"
      66        
54             if $pkg && $package && $pkg ne $package;
55              
56 1490   66     3500 $symbol->{package} ||= $caller;
57              
58 1490         4648 return $symbol;
59             }
60              
61 634 50       1409 utf8::downgrade($symbol) if $] == 5.010000; # prevent crash on 5.10.0
62 634 50       4415 my ($sig, $pkg, $name) = ($symbol =~ m/^(\W?)(.*::)?([^:]+)$/)
63             or croak "Invalid symbol: '$symbol'";
64              
65             # Normalize package, '::' becomes 'main', 'Foo::' becomes 'Foo'
66 634 100       1669 $pkg = $pkg
    100          
67             ? $pkg eq '::'
68             ? 'main'
69             : substr($pkg, 0, -2)
70             : undef;
71              
72 634 100 100     1728 croak "Symbol package ($pkg) and package argument ($package) do not match"
      66        
73             if $pkg && $package && $pkg ne $package;
74              
75 633   100     2027 $sig ||= '&';
76 633   33     1684 my $type = $SIGMAP{$sig} || croak "unsupported sigil: '$sig'";
77              
78 633   66     1492 my $real_package = $package || $pkg || $caller;
79              
80             return {
81 633         3626 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 936     936 1 2296 my $sym = _parse_symbol(scalar(caller), @_);
91              
92 936         1774 my $name = $sym->{name};
93 936         1445 my $type = $sym->{type};
94 936         1358 my $package = $sym->{package};
95 936         1376 my $symbol = $sym->{symbol};
96              
97 936         1849 my $stash = get_stash($package);
98 936 100       2388 return undef unless exists $stash->{$name};
99              
100 874         1652 my $glob = get_glob($sym);
101 874 100 100     2175 return *{$glob}{$type} if $type ne 'SCALAR' && defined(*{$glob}{$type});
  393         1959  
  779         2423  
102              
103 481 50       966 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 481         1689 my $sv = B::svref_2object($glob)->SV;
118 481 100       1772 return *{$glob}{$type} if $sv->isa('B::SV');
  88         258  
119 393 50       1207 return undef unless $sv->isa('B::SPECIAL');
120 393 50       845 return *{$glob}{$type} if $B::specialsv_name[$$sv] ne 'Nullsv';
  0         0  
121 393         848 return undef;
122             }
123              
124             sub purge_symbol {
125 88     88 1 416 my $sym = _parse_symbol(scalar(caller), @_);
126              
127 88         160 local *GLOBCLONE = *{get_glob($sym)};
  88         214  
128 88         257 delete get_stash($sym->{package})->{$sym->{name}};
129 88         187 my $new_glob = get_glob($sym);
130              
131 88         217 for my $type (qw/CODE SCALAR HASH ARRAY FORMAT IO/) {
132 528 100       1100 next if $type eq $sym->{type};
133 440         1318 my $ref = get_symbol({type => $type, name => 'GLOBCLONE', sigil => $SLOTMAP{$type}}, __PACKAGE__);
134 440 100       1242 next unless $ref;
135 21         47 *$new_glob = $ref;
136             }
137              
138 88         735 return *GLOBCLONE{$sym->{type}};
139             }
140              
141             1;
142              
143             __END__