File Coverage

blib/lib/Symbol/Methods.pm
Criterion Covered Total %
statement 90 97 92.7
branch 21 26 80.7
condition 15 19 78.9
subroutine 19 19 100.0
pod n/a
total 145 161 90.0


line stmt bran cond sub pod time code
1             package Symbol::Methods;
2 6     6   41565 use strict;
  6         12  
  6         153  
3 6     6   31 use warnings;
  6         11  
  6         166  
4              
5 6     6   42 use Carp qw/croak/;
  6         10  
  6         411  
6 6     6   31 use B;
  6         9  
  6         4458  
7              
8             our $VERSION = '0.000001';
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 28     28   73 my ($class, $sym) = @_;
26 28         63 $sym = _parse_symbol($sym, $class);
27 28         62 my $ref = _get_ref($sym);
28 28 100       178 return $ref ? 1 : 0;
29             }
30              
31             sub symbol::fetch {
32 8     8   23 my ($class, $sym) = @_;
33 8         26 $sym = _parse_symbol($sym, $class);
34 8         25 return _get_ref($sym);
35             }
36              
37             sub symbol::delete {
38 8     8   22 my ($class, $sym) = @_;
39 8         26 $sym = _parse_symbol($sym, $class);
40 8         25 my $ref = _get_ref($sym);
41 8         27 _purge_symbol($sym);
42 8         3381 return $ref;
43             }
44              
45             sub symbol::alias {
46 20     20   37 my ($class, $old_sym, $new_sym) = @_;
47 20         39 $old_sym = _parse_symbol($old_sym, $class);
48 20         46 $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       489 unless $old_sym->{type} eq $new_sym->{type};
52              
53 18 100       38 my $old_ref = _get_ref($old_sym) or croak "Symbol $old_sym->{sym} does not exist";
54 16 100       31 my $new_ref = _get_ref($new_sym) and croak "Symbol $new_sym->{sym} already exists";
55              
56 13         17 *{_get_glob($new_sym)} = $old_ref;
  13         26  
57             }
58              
59             sub symbol::move {
60 9     9   20 my ($class, $old_sym, $new_sym) = @_;
61 9         21 $old_sym = _parse_symbol($old_sym, $class);
62 9         29 $new_sym = _parse_symbol($new_sym, $class, $old_sym->{sigil});
63              
64 9         20 symbol::alias($class, $old_sym, $new_sym);
65              
66 6         15 _purge_symbol($old_sym);
67             }
68              
69             sub _parse_symbol {
70 140     140   8775 my ($sym, $class, $def_sig) = @_;
71 140 100       351 return $sym if ref $sym;
72              
73 122         676 my ($sig, $pkg, $name) = ($sym =~ m/^(\W)?(.*::)?([^:]+)$/);
74              
75 122   100     454 $sig ||= $def_sig || '&';
      66        
76              
77 122   66     450 $pkg ||= $class;
78 122 100       288 $pkg = 'main' if $pkg eq '::';
79 122         195 $pkg =~ s/::$//;
80              
81 122   66     573 my $type = $SIGMAP{$sig} || croak "Unsupported sigil '$sig'";
82              
83             return {
84 121         767 sym => "$sig$pkg\::$name",
85             name => $name,
86             sigil => $sig,
87             type => $type,
88             pkg => $pkg,
89             };
90             }
91              
92             sub _get_stash {
93 19     19   29 my ($sym) = @_;
94 6     6   38 no strict 'refs';
  6         9  
  6         216  
95 6     6   29 no warnings 'once';
  6         10  
  6         529  
96 19         27 return \%{"$sym->{pkg}\::"};
  19         88  
97             }
98              
99             sub _get_glob {
100 152     152   203 my ($sym) = @_;
101 6     6   39 no strict 'refs';
  6         12  
  6         206  
102 6     6   31 no warnings 'once';
  6         10  
  6         2638  
103 152         185 return \*{"$sym->{pkg}\::$sym->{name}"};
  152         2326  
104             }
105              
106             sub _get_ref {
107 191     191   298 my ($sym, $globref) = @_;
108 191   66     497 $globref ||= _get_glob($sym);
109 191         328 my $type = $sym->{type};
110              
111 191 100 100     498 return *{$globref}{$type} if $type ne 'SCALAR' && defined(*{$globref}{$type});
  52         408  
  147         594  
112              
113 139 50       336 if ($] < 5.010) {
114 0 0       0 return *{$globref}{$type} if eval { defined(${ *{$globref}{$type} }) };
  0         0  
  0         0  
  0         0  
  0         0  
115 0         0 return undef;
116             }
117              
118 139         600 my $sv = B::svref_2object($globref)->SV;
119 139 100       718 return *{$globref}{$type} if $sv->isa('B::SV');
  72         692  
120 67 50       296 return undef unless $sv->isa('B::SPECIAL');
121 67 50       179 return *{$globref}{$type} if $B::specialsv_name[$$sv] ne 'Nullsv';
  0         0  
122 67         562 return undef;
123             }
124              
125             sub _set_symbol {
126 1     1   4 my ($sym, $ref) = @_;
127 1         2 *{_get_glob($sym)} = $ref;
  1         2  
128             }
129              
130             sub _purge_symbol {
131 18     18   60 my ($sym) = @_;
132              
133 18         28 local *GLOBCLONE = *{_get_glob($sym)};
  18         42  
134 18         43 delete _get_stash($sym)->{$sym->{name}};
135 18         44 my $new_glob = _get_glob($sym);
136              
137 18         44 for my $type (qw/CODE SCALAR HASH ARRAY FORMAT IO/) {
138 108 100       287 next if $type eq $sym->{type};
139 90   100     285 my $ref = _get_ref({type => $type}, \*GLOBCLONE) || next;
140 18         54 *$new_glob = $ref;
141             }
142              
143 18         1665 return *GLOBCLONE{$sym->{type}};
144             }
145              
146             1;
147              
148             __END__