File Coverage

blib/lib/Method/Lexical.pm
Criterion Covered Total %
statement 97 115 84.3
branch 24 42 57.1
condition 7 14 50.0
subroutine 15 19 78.9
pod n/a
total 143 190 75.2


line stmt bran cond sub pod time code
1             package Method::Lexical;
2              
3 9     9   329665 use 5.008001;
  9         39  
  9         701  
4              
5 9     9   61 use strict;
  9         17  
  9         553  
6 9     9   51 use warnings;
  9         23  
  9         1123  
7              
8 9     9   11897 use B::Hooks::EndOfScope;
  9         175443  
  9         79  
9 9     9   12869 use B::Hooks::OP::Check;
  9         24027  
  9         487  
10 9     9   10883 use B::Hooks::OP::Annotation;
  9         12403  
  9         361  
11 9     9   76 use Carp qw(carp confess);
  9         129  
  9         1410  
12 9     9   11227 use Devel::Pragma qw(ccstash fqname my_hints new_scope on_require);
  9         38848  
  9         205  
13 9     9   2636 use XSLoader;
  9         23  
  9         3464  
14              
15             our $VERSION = '0.30';
16             our @CARP_NOT = qw(B::Hooks::EndOfScope);
17              
18             XSLoader::load(__PACKAGE__, $VERSION);
19              
20             my $DEBUG = xs_get_debug(); # flag indicating whether debug messages should be printed
21              
22             # The key under which the $installed hash is installed in %^H i.e. 'Method::Lexical'
23             # Defined as a preprocessor macro in Lexical.xs to ensure the Perl and XS are kept in sync
24             my $METHOD_LEXICAL = xs_signature();
25              
26             # accessors for the debug flags - note there is one for Perl ($DEBUG) and one defined
27             # in the XS (METHOD_LEXICAL_DEBUG). The accessors ensure that the two are kept in sync
28 0     0   0 sub _get_debug() { $DEBUG }
29 0   0 0   0 sub _set_debug($) { xs_set_debug($DEBUG = shift || 0) }
30              
31             # This logs method installations/uninstallations
32             sub _debug {
33 0     0   0 my ($class, $action, $fqname) = @_;
34 0         0 carp "$class: $action $fqname";
35             }
36              
37             # return true if $ref ISA $class - works with non-references, unblessed references and objects
38             sub _isa($$) {
39 29     29   61 my ($ref, $class) = @_;
40 29 50       190 return Scalar::Util::blessed(ref) ? $ref->isa($class) : ref($ref) eq $class;
41             }
42              
43             # given a fully-qualified subroutine name (e.g. Foo::Bar::baz) load the module (Foo::Bar)
44             sub _load($) {
45 6     6   16 my $fqname = shift;
46 6         17 my ($module, $subname) = fqname($fqname);
47              
48 6         702 eval "require $module";
49              
50 6 50       51 if ($@) {
51 9     9   146 no strict 'refs';
  9         19  
  9         7419  
52             # don't raise an error if the package is declared in an already-loaded file
53 6 100       9 confess "Can't load $module for subroutine $fqname: $@" unless (%{"$module\::"});
  6         862  
54             }
55             }
56              
57             # install one or more lexical methods in the current scope
58             #
59             # import() has to keep track of two things:
60             #
61             # 1) $installed keeps track of *all* currently active lexical methods so that Lexical.xs
62             # can track them without needing to know the subclass of Method::Lexical that installed them
63             # 2) $class_data keeps track of which subs have been installed by this class (which may be a subclass of
64             # Method::Lexical) in this scope, so that they can be unimported with "no MyPragma (...)"
65              
66             sub import {
67 14     14   2297 my $class = shift;
68 14 100 66     120 my %bindings = ((@_ == 1) && _isa($_[0], 'HASH')) ? %{shift()} : @_; # hash or hashref
  6         45  
69              
70 14 100       86 return unless (%bindings);
71              
72 13         45 my $autoload = delete $bindings{-autoload};
73 13         25 my $debug = delete $bindings{-debug};
74 13         106 my $hints = my_hints;
75 13         107 my $caller = ccstash();
76 13         18 my $installed;
77              
78 13 50       53 if (defined $debug) {
79 0         0 my $old_debug = _get_debug();
80 0 0       0 if ($debug != $old_debug) {
81 0         0 _set_debug($debug);
82 0     0   0 on_scope_end { _set_debug($old_debug) };
  0         0  
83             }
84             }
85              
86 13 50       47 if (new_scope($METHOD_LEXICAL)) {
87 13         470 my $top_level = 0;
88 13         31 my $temp = $hints->{$METHOD_LEXICAL};
89              
90 13 50       199 if ($temp) {
91             # the hash is cloned to ensure that inner/nested scopes don't clobber/contaminate
92             # outer/previous scopes with their new bindings. Likewise, unimport installs
93             # a new hash to ensure that previous bindings aren't clobbered e.g.
94             #
95             # {
96             # package Foo;
97             #
98             # use Method::Lexical bar => sub { ... };
99             #
100             # Foo->new->bar();
101             #
102             # no Method::Lexical; # don't clobber the bindings associated with the previous method call
103             # }
104              
105 0         0 $installed = $hints->{$METHOD_LEXICAL} = { %$temp }; # clone
106             } else {
107 13         18 $top_level = 1;
108 13         76 $installed = $hints->{$METHOD_LEXICAL} = {}; # create
109              
110             # disable Method::Lexical altogether when we leave the top-level scope in which it was enabled
111 13         72 on_scope_end \&xs_leave;
112              
113             # disable/re-enable check hooks before/after require
114 13         428 on_require \&xs_leave, \&xs_enter;
115              
116 13         725 xs_enter();
117             }
118             } else {
119 0         0 $installed = $hints->{$METHOD_LEXICAL}; # augment
120             }
121              
122             # Note: the class-specific data is stored under "Method::Lexical($subclass)" rather than
123             # $subclass. The subclass might well have its own uses for $^H{$subclass}, so we keep
124             # our mitts off it
125             #
126             # Also, the unadorned class name can't be used as a key if $METHOD_LEXICAL is 'Method::Lexical' (which
127             # it is) as the two uses conflict with and clobber each other
128              
129 13         39 my $subclass = "$METHOD_LEXICAL($class)";
130 13         43 my $class_data;
131              
132             # never use $class as the identifier for new_scope() here - see above
133 13 50       38 if (new_scope($subclass)) {
134 13         322 my $temp = $hints->{$subclass};
135              
136 13 50       84 $class_data = $hints->{$subclass} = $temp ? { %$temp } : {}; # clone/create
137             } else {
138 0         0 $class_data = $hints->{$subclass}; # augment
139             }
140              
141 13         46 for my $name (keys %bindings) {
142 23         41 my $sub = $bindings{$name};
143              
144             # normalize bindings
145 23 100       59 unless (_isa($sub, 'CODE')) {
146 8         24 my $_autoload = $sub =~ s{^\+}{}; # autoload this sub's package
147 8         25 my $subname = fqname($sub); # XXX watch out for fqname returning a list
148              
149 8 100 100     162 if ($_autoload || $autoload) {
150 6         15 _load($subname);
151             }
152              
153 6   33     8 $sub = do {
154 9     9   76 no strict 'refs';
  9         18  
  9         5791  
155             *{$subname}{CODE}
156             } || confess "Can't find subroutine for target $name: '$subname'";
157             }
158              
159 21         77 my $fqname = fqname($name, $caller);
160              
161 21 50       489 if ($DEBUG) {
162 0 0       0 if (exists $installed->{$fqname}) {
163 0         0 $class->_debug('redefining', $fqname);
164             } else {
165 0         0 $class->_debug('creating', $fqname);
166             }
167             }
168              
169 21         54 $installed->{$fqname} = $sub;
170 21         7078 $class_data->{$fqname} = $sub;
171             }
172             }
173              
174             # uninstall one or more lexical subs from the current scope
175             sub unimport {
176 1     1   8 my $class = shift;
177 1         3 my $hints = my_hints;
178 1         7 my $subclass = "$METHOD_LEXICAL($class)";
179 1         1 my $class_data;
180              
181 1 50 33     15 return unless (($^H & 0x20000) && ($class_data = $hints->{$subclass}));
182              
183 1         9 my $caller = ccstash();
184 1 50       6 my @subs = @_ ? (map { scalar(fqname($_, $caller)) } @_) : keys(%$class_data);
  0         0  
185 1         3 my $installed = $hints->{$METHOD_LEXICAL};
186 1         2 my $new_installed = { %$installed }; # clone
187 1         2 my $deleted = 0;
188              
189 1         2 for my $fqname (@subs) {
190 1         2 my $sub = $class_data->{$fqname};
191              
192 1 50       5 if ($sub) { # the coderef of the method this subclass installed
193             # if the current sub ($installed->{$fqname}) is the sub this module installed ($class_data->{$fqname})
194 1 50       5 if (Scalar::Util::refaddr($sub) == Scalar::Util::refaddr($installed->{$fqname})) {
195 1 50       3 $class->_debug('unimporting', $fqname) if ($DEBUG);
196              
197             # what import adds, unimport taketh away
198 1         2 delete $new_installed->{$fqname};
199 1         2 delete $class_data->{$fqname};
200              
201 1         2 ++$deleted;
202             } else {
203 0         0 carp "$class: attempt to unimport a shadowed lexical method: $fqname";
204             }
205             } else {
206 0         0 carp "$class: attempt to unimport an undefined lexical method: $fqname";
207             }
208             }
209              
210 1 50       4 if ($deleted) {
211 1         176 $hints->{$METHOD_LEXICAL} = $new_installed;
212             }
213             }
214              
215             1;
216              
217             __END__