File Coverage

blib/lib/Symbol/Rename.pm
Criterion Covered Total %
statement 90 92 97.8
branch 33 40 82.5
condition 4 6 66.6
subroutine 17 17 100.0
pod 0 1 0.0
total 144 156 92.3


line stmt bran cond sub pod time code
1             package Symbol::Rename;
2 2     2   48268 use strict;
  2         6  
  2         62  
3 2     2   12 use warnings;
  2         3  
  2         67  
4              
5 2     2   11 use Carp qw/croak/;
  2         9  
  2         141  
6 2     2   12 use B;
  2         4  
  2         1483  
7              
8             our $VERSION = '0.000002';
9              
10             my %SIGMAP = (
11             '&' => 'CODE',
12             '$' => 'SCALAR',
13             '%' => 'HASH',
14             '@' => 'ARRAY',
15             # Others are unsupported.
16             );
17             my %TYPEMAP = reverse %SIGMAP;
18              
19             sub import {
20 9     9   3750 my $class = shift;
21 9 50       44 return unless @_;
22 9         30 my ($opts, %renames) = _parse_args(@_);
23              
24 8   66     57 my $package = delete $opts->{package} || caller;
25              
26 8 100       225 croak "Unsupported option(s): " . join(", ", keys %$opts)
27             if keys %$opts;
28              
29 7         27 _rename_symbols($package, %renames);
30             }
31              
32             sub rename_symbols {
33 2     2 0 6 my $class = shift;
34 2         10 my ($opts, %renames) = _parse_args(@_);
35              
36 2 50       13 croak "Unsupported options: " . join(", ", keys %$opts)
37             if keys %$opts;
38              
39 2         10 _rename_symbols($class, %renames);
40             }
41              
42             sub _parse_args {
43 11     11   23 my (%opts, %renames);
44              
45 11         50 while (my $symbol = shift @_) {
46 17 100       77 if ($symbol =~ m/^-(.*)$/) {
47 5 100       328 $opts{$1} = shift @_ or croak "No argument provided for option '-$1'";
48 4         16 next;
49             }
50              
51 12         81 my ($sig, $name) = ($symbol =~ qw/^(\W?)(.*)$/);
52 12 100       49 my $type = $sig ? $SIGMAP{$sig} : 'CODE';
53 12 50       39 croak "Unsupported sigil ($sig) on symbol '$symbol'" unless $type;
54              
55 12 50       42 my $new_name = shift(@_) or croak "No new name was specified for symbol '$symbol'";
56              
57             croak "Symbol '$symbol' ($type, $name) listed multiple times"
58 12 50       51 if $renames{$name}->{$type};
59              
60 12         73 $renames{$name}->{$type} = $new_name;
61             }
62              
63 10         55 return (\%opts, %renames);
64             }
65              
66             sub _symbol_exists {
67 62     62   143 my ($globref, $type) = @_;
68              
69 62 100       210 return defined(*{$globref}{$type})
  46         730  
70             unless $type eq 'SCALAR';
71              
72 16 50       48 return defined(${ *{$globref}{$type} })
  0         0  
  0         0  
73             if $] < 5.010;
74              
75 16         112 my $sv = B::svref_2object($globref)->SV;
76 16 100       151 return 1 if $sv->isa('B::SV');
77 8 50       41 return 0 unless $sv->isa('B::SPECIAL');
78 8         54 return $B::specialsv_name[$$sv] ne 'Nullsv';
79             }
80              
81             sub _rename_symbols {
82 9     9   16 my $class = shift;
83 9         26 my %renames = @_;
84              
85 2     2   12 no strict 'refs';
  2         4  
  2         165  
86 9         17 my $stash = \%{"$class\::"};
  9         32  
87 2     2   10 use strict 'refs';
  2         4  
  2         97  
88              
89 9         39 for my $name (keys %renames) {
90 9         17 my $types = $renames{$name};
91              
92 2     2   10 no strict 'refs';
  2         4  
  2         135  
93 9         16 local *GLOBCLONE = *{"$class\::$name"};
  9         77  
94 2     2   10 use strict 'refs';
  2         5  
  2         313  
95              
96 9         33 delete $stash->{$name};
97              
98 9         23 for my $type (qw/CODE SCALAR HASH ARRAY FORMAT IO/) {
99 39 100       3135 if (my $new_name = $types->{$type}) {
    100          
    100          
100 12 100       40 croak "Origin symbol '$TYPEMAP{$type}$class\::$name' does not exist"
101             unless _symbol_exists(\*GLOBCLONE, $type);
102              
103 11 100       44 if (my $ref = ref($new_name)) {
104 2 100       206 croak "'$ref' references cannot be used as a renaming destination for '$TYPEMAP{$type}$class\::$name'"
105             unless $ref eq 'SCALAR';
106              
107 1         2 $$new_name = *GLOBCLONE{$type};
108              
109 1         3 next;
110             }
111              
112 2     2   11 no strict 'refs';
  2         3  
  2         113  
113 9         14 my $destglob = \*{"$class\::$new_name"};
  9         35  
114 2     2   18 use strict 'refs';
  2         4  
  2         393  
115              
116 9 100 66     40 croak "Destination symbol '$TYPEMAP{$type}$class\::$new_name' already exists"
117             if !ref($new_name) && _symbol_exists($destglob, $type);
118              
119 2     2   12 no strict 'refs';
  2         4  
  2         194  
120 8         21 *{"$class\::$new_name"} = *GLOBCLONE{$type};
  8         37  
121             }
122 3         12 elsif($type eq 'SCALAR' ? defined ${*GLOBCLONE{$type}} : defined *GLOBCLONE{$type}) {
123 2     2   11 no strict 'refs';
  2         4  
  2         218  
124 6         10 *{"$class\::$name"} = *GLOBCLONE{$type};
  6         21  
125             }
126             }
127             }
128             }
129              
130             1;
131              
132             __END__