File Coverage

blib/lib/Symbol/Methods.pm
Criterion Covered Total %
statement 94 107 87.8
branch 26 36 72.2
condition 13 19 68.4
subroutine 19 20 95.0
pod n/a
total 152 182 83.5


line stmt bran cond sub pod time code
1             package Symbol::Methods;
2 6     6   42033 use strict;
  6         13  
  6         144  
3 6     6   29 use warnings;
  6         10  
  6         163  
4              
5 6     6   34 use Carp qw/croak/;
  6         12  
  6         406  
6 6     6   30 use B;
  6         9  
  6         4464  
7              
8             our $VERSION = '0.000002';
9             our @CARP_NOT = (
10             'Symbol::Alias',
11             'Symbol::Delete',
12             'Symbol::Extract',
13             'Symbol::Move',
14             );
15              
16             my %SIGMAP = (
17             '&' => 'CODE',
18             '$' => 'SCALAR',
19             '%' => 'HASH',
20             '@' => 'ARRAY',
21             # Others are unsupported.
22             );
23              
24             sub symbol::exists {
25 29     29   74 my ($class, $sym) = @_;
26 29         64 $sym = _parse_symbol($sym, $class);
27 29         69 my $ref = _get_ref($sym);
28 29 100       196 return $ref ? 1 : 0;
29             }
30              
31             sub symbol::fetch {
32 8     8   19 my ($class, $sym) = @_;
33 8         23 $sym = _parse_symbol($sym, $class);
34 8         21 return _get_ref($sym);
35             }
36              
37             sub symbol::delete {
38 8     8   18 my ($class, $sym) = @_;
39 8         20 $sym = _parse_symbol($sym, $class);
40 8         20 my $ref = _get_ref($sym);
41 8         23 _purge_symbol($sym);
42 8         2959 return $ref;
43             }
44              
45             sub symbol::alias {
46 20     20   38 my ($class, $old_sym, $new_sym) = @_;
47 20         43 $old_sym = _parse_symbol($old_sym, $class);
48 20         50 $new_sym = _parse_symbol($new_sym, $class, $old_sym->{sigil});
49              
50             croak "Origin and Destination symbols must be the same type, got '$old_sym->{type}' and '$new_sym->{type}'"
51 20 100       525 unless $old_sym->{type} eq $new_sym->{type};
52              
53 18 100       36 my $old_ref = _get_ref($old_sym) or croak "Symbol $old_sym->{sym} does not exist";
54 16 100       32 my $new_ref = _get_ref($new_sym) and croak "Symbol $new_sym->{sym} already exists";
55              
56 13         16 *{_get_glob($new_sym)} = $old_ref;
  13         26  
57             }
58              
59             sub symbol::move {
60 9     9   22 my ($class, $old_sym, $new_sym) = @_;
61 9         21 $old_sym = _parse_symbol($old_sym, $class);
62 9         23 $new_sym = _parse_symbol($new_sym, $class, $old_sym->{sigil});
63              
64 9         22 symbol::alias($class, $old_sym, $new_sym);
65              
66 6         14 _purge_symbol($old_sym);
67             }
68              
69             sub _parse_symbol {
70 141     141   11856 my ($sym, $class, $def_sig) = @_;
71 141 100       374 return $sym if ref $sym;
72              
73 123         747 my ($sig, $pkg, $name) = ($sym =~ m/^(\W)?(.*::)?([^:]+)$/);
74              
75 123   100     440 $sig ||= $def_sig || '&';
      66        
76              
77 123   66     453 $pkg ||= $class;
78 123 100       277 $pkg = 'main' if $pkg eq '::';
79 123         198 $pkg =~ s/::$//;
80              
81 123   66     549 my $type = $SIGMAP{$sig} || croak "Unsupported sigil '$sig'";
82              
83             return {
84 122         831 sym => "$sig$pkg\::$name",
85             name => $name,
86             sigil => $sig,
87             type => $type,
88             pkg => $pkg,
89             };
90             }
91              
92             sub _get_stash {
93 121     121   167 my ($sym) = @_;
94 6     6   34 no strict 'refs';
  6         11  
  6         190  
95 6     6   30 no warnings 'once';
  6         8  
  6         524  
96 121         137 return \%{"$sym->{pkg}\::"};
  121         438  
97             }
98              
99             sub _get_glob {
100 135     135   181 my ($sym) = @_;
101 6     6   34 no strict 'refs';
  6         10  
  6         185  
102 6     6   27 no warnings 'once';
  6         9  
  6         3496  
103 135         147 return \*{"$sym->{pkg}\::$sym->{name}"};
  135         2059  
104             }
105              
106             sub _get_ref {
107 192     192   298 my ($sym, $globref) = @_;
108              
109 192 100       439 unless($sym->{NO_CHECK_STASH}) {
110 102         183 my $stash = _get_stash($sym);
111 102 100       650 return undef unless exists $stash->{$sym->{name}};
112              
113 84         150 $globref = _get_glob($sym);
114             }
115              
116 174 50       354 croak "You must pass in a globref for this usage" unless $globref;
117              
118 174         250 my $type = $sym->{type};
119              
120 174 100 100     427 return *{$globref}{$type} if $type ne 'SCALAR' && defined(*{$globref}{$type});
  52         410  
  135         523  
121              
122 122 50       302 if ($] < 5.010) {
123 0 0       0 unless ($sym->{NO_CHECK_STASH}) {
124 0         0 local $@;
125 0     0   0 local $SIG{__WARN__} = sub { 1 };
  0         0  
126 0 0       0 return *{$globref}{$type} if eval "package $sym->{pkg}; my \$y = $sym->{sigil}$sym->{name}; 1";
  0         0  
127             }
128 0 0 0     0 return *{$globref}{$type} if defined(*{$globref}{$type}) && defined(${*{$globref}{$type}});
  0         0  
  0         0  
  0         0  
  0         0  
129 0         0 return undef;
130             }
131              
132 122         532 my $sv = B::svref_2object($globref)->SV;
133 122 100       612 return *{$globref}{$type} if $sv->isa('B::SV');
  73         691  
134 49 50       155 return undef unless $sv->isa('B::SPECIAL');
135 49 50       146 return *{$globref}{$type} if $B::specialsv_name[$$sv] ne 'Nullsv';
  0         0  
136 49         187 return undef;
137             }
138              
139             sub _set_symbol {
140 1     1   2 my ($sym, $ref) = @_;
141 1         3 *{_get_glob($sym)} = $ref;
  1         3  
142             }
143              
144             sub _purge_symbol {
145 18     18   31 my ($sym) = @_;
146              
147 18         23 local *GLOBCLONE = *{_get_glob($sym)};
  18         38  
148 18         40 delete _get_stash($sym)->{$sym->{name}};
149 18         39 my $new_glob = _get_glob($sym);
150              
151 18         40 for my $type (qw/CODE SCALAR HASH ARRAY FORMAT IO/) {
152 108 100       265 next if $type eq $sym->{type};
153 90   100     291 my $ref = _get_ref({type => $type, NO_CHECK_STASH => 1}, \*GLOBCLONE) || next;
154 18         46 *$new_glob = $ref;
155             }
156              
157 18         1615 return *GLOBCLONE{$sym->{type}};
158             }
159              
160             1;
161              
162             __END__