File Coverage

blib/lib/Package/Stash/PP.pm
Criterion Covered Total %
statement 195 204 95.5
branch 69 84 82.1
condition 36 44 81.8
subroutine 35 35 100.0
pod 1 11 9.0
total 336 378 88.8


line stmt bran cond sub pod time code
1             package Package::Stash::PP;
2 24     24   15497 use strict;
  24         54  
  24         725  
3 24     24   121 use warnings;
  24         48  
  24         1110  
4             # ABSTRACT: pure perl implementation of the Package::Stash API
5              
6             our $VERSION = '0.38';
7              
8 24     24   146 use B;
  24         44  
  24         1111  
9 24     24   167 use Carp qw(confess);
  24         43  
  24         1307  
10 24     24   169 use Scalar::Util qw(blessed reftype weaken);
  24         64  
  24         1388  
11 24     24   10483 use Symbol;
  24         18116  
  24         1724  
12             # before 5.12, assigning to the ISA glob would make it lose its magical ->isa
13             # powers
14 24     24   179 use constant BROKEN_ISA_ASSIGNMENT => ($] < 5.012);
  24         48  
  24         1629  
15             # before 5.10, stashes don't ever seem to drop to a refcount of zero, so
16             # weakening them isn't helpful
17 24     24   145 use constant BROKEN_WEAK_STASH => ($] < 5.010);
  24         47  
  24         1267  
18             # before 5.10, the scalar slot was always treated as existing if the
19             # glob existed
20 24     24   137 use constant BROKEN_SCALAR_INITIALIZATION => ($] < 5.010);
  24         62  
  24         1165  
21             # add_method on anon stashes triggers rt.perl #1804 otherwise
22             # fixed in perl commit v5.13.3-70-g0fe688f
23 24     24   130 use constant BROKEN_GLOB_ASSIGNMENT => ($] < 5.013004);
  24         43  
  24         1183  
24             # pre-5.10, ->isa lookups were cached in the ::ISA::CACHE:: slot
25 24     24   127 use constant HAS_ISA_CACHE => ($] < 5.010);
  24         50  
  24         7484  
26              
27              
28             sub new {
29 62     62 0 36517 my $class = shift;
30 62         163 my ($package) = @_;
31              
32 62 100 100     886 if (!defined($package) || (ref($package) && reftype($package) ne 'HASH')) {
    100 100        
    100 66        
33 4         569 confess "Package::Stash->new must be passed the name of the "
34             . "package to access";
35             }
36             elsif (ref($package) && reftype($package) eq 'HASH') {
37 1         2 confess "The PP implementation of Package::Stash does not support "
38             . "anonymous stashes before perl 5.14"
39             if BROKEN_GLOB_ASSIGNMENT;
40              
41 1         5 return bless {
42             'namespace' => $package,
43             }, $class;
44             }
45             elsif ($package =~ /\A[0-9A-Z_a-z]+(?:::[0-9A-Z_a-z]+)*\z/) {
46 47         247 return bless {
47             'package' => $package,
48             }, $class;
49             }
50             else {
51 10         1840 confess "$package is not a module name";
52             }
53              
54             }
55              
56             sub name {
57 49 100   49 0 1970 confess "Can't call name as a class method"
58             unless blessed($_[0]);
59             confess "Can't get the name of an anonymous package"
60 47 50       148 unless defined($_[0]->{package});
61 47         251 return $_[0]->{package};
62             }
63              
64             sub namespace {
65 372 50   372 0 1277 confess "Can't call namespace as a class method"
66             unless blessed($_[0]);
67              
68 372         594 if (BROKEN_WEAK_STASH) {
69 24     24   185 no strict 'refs';
  24         57  
  24         2033  
70             return \%{$_[0]->name . '::'};
71             }
72             else {
73 372 100       1078 return $_[0]->{namespace} if defined $_[0]->{namespace};
74              
75             {
76 24     24   168 no strict 'refs';
  24         44  
  24         14468  
  45         79  
77 45         66 $_[0]->{namespace} = \%{$_[0]->name . '::'};
  45         111  
78             }
79              
80 45         166 weaken($_[0]->{namespace});
81              
82 45         96 return $_[0]->{namespace};
83             }
84             }
85              
86             {
87             my %SIGIL_MAP = (
88             '$' => 'SCALAR',
89             '@' => 'ARRAY',
90             '%' => 'HASH',
91             '&' => 'CODE',
92             '' => 'IO',
93             );
94              
95             sub _deconstruct_variable_name {
96 359     359   637 my ($variable) = @_;
97              
98 359         517 my @ret;
99 359 100       815 if (ref($variable) eq 'HASH') {
100 88         128 @ret = @{$variable}{qw[name sigil type]};
  88         216  
101             }
102             else {
103 271 50 33     1181 (defined $variable && length $variable)
104             || confess "You must pass a variable name";
105              
106 271         701 my $sigil = substr($variable, 0, 1, '');
107              
108 271 100       583 if (exists $SIGIL_MAP{$sigil}) {
109 245         704 @ret = ($variable, $sigil, $SIGIL_MAP{$sigil});
110             }
111             else {
112 26         79 @ret = ("${sigil}${variable}", '', $SIGIL_MAP{''});
113             }
114             }
115              
116             # XXX in pure perl, this will access things in inner packages,
117             # in xs, this will segfault - probably look more into this at
118             # some point
119 359 100       1527 ($ret[0] !~ /::/)
120             || confess "Variable names may not contain ::";
121              
122 356         1218 return @ret;
123             }
124             }
125              
126             sub _valid_for_type {
127 68     68   129 my ($value, $type) = @_;
128 68 100 100     390 if ($type eq 'HASH' || $type eq 'ARRAY'
      100        
      100        
129             || $type eq 'IO' || $type eq 'CODE') {
130 43         705 return reftype($value) eq $type;
131             }
132             else {
133 25         68 my $ref = reftype($value);
134 25   100     903 return !defined($ref) || $ref eq 'SCALAR' || $ref eq 'REF' || $ref eq 'LVALUE' || $ref eq 'REGEXP' || $ref eq 'VSTRING';
135             }
136             }
137              
138             sub add_symbol {
139 78     78 0 28914 my ($self, $variable, $initial_value, %opts) = @_;
140              
141 78         193 my ($name, $sigil, $type) = _deconstruct_variable_name($variable);
142              
143 77 100       217 if (@_ > 2) {
144 68 100       149 _valid_for_type($initial_value, $type)
145             || confess "$initial_value is not of type $type";
146              
147             # cheap fail-fast check for PERLDBf_SUBLINE and '&'
148 59 100 66     329 if ($^P and $^P & 0x10 && $sigil eq '&') {
      66        
149 2         5 my $filename = $opts{filename};
150 2         4 my $first_line_num = $opts{first_line_num};
151              
152 2 100       7 (undef, $filename, $first_line_num) = caller
153             if not defined $filename;
154              
155 2   66     8 my $last_line_num = $opts{last_line_num} || ($first_line_num ||= 0);
156              
157             # http://perldoc.perl.org/perldebguts.html#Debugger-Internals
158 2         9 $DB::sub{$self->name . '::' . $name} = "$filename:$first_line_num-$last_line_num";
159             }
160             }
161              
162 68         118 if (BROKEN_GLOB_ASSIGNMENT) {
163             if (@_ > 2) {
164 24     24   186 no strict 'refs';
  24         62  
  24         880  
165 24     24   157 no warnings 'redefine';
  24         51  
  24         1559  
166             *{ $self->name . '::' . $name } = ref $initial_value
167             ? $initial_value : \$initial_value;
168             }
169             else {
170 24     24   157 no strict 'refs';
  24         53  
  24         2665  
171             if (BROKEN_ISA_ASSIGNMENT && $name eq 'ISA') {
172             *{ $self->name . '::' . $name };
173             }
174             else {
175             my $undef = _undef_ref_for_type($type);
176             *{ $self->name . '::' . $name } = $undef;
177             }
178             }
179             }
180             else {
181 68         153 my $namespace = $self->namespace;
182             {
183             # using glob aliasing instead of Symbol::gensym, because otherwise,
184             # magic doesn't get applied properly.
185             # see <20120710063744.19360.qmail@lists-nntp.develooper.com> on p5p
186 68         107 local *__ANON__:: = $namespace;
  68         244  
187 24     24   182 no strict 'refs';
  24         66  
  24         912  
188 24     24   532 no warnings 'void';
  24         79  
  24         1005  
189 24     24   144 no warnings 'once';
  24         55  
  24         2018  
190 68         118 *{"__ANON__::$name"};
  68         302  
191             }
192              
193 68 100       204 if (@_ > 2) {
194 24     24   164 no warnings 'redefine';
  24         46  
  24         13349  
195 59 100       141 *{ $namespace->{$name} } = ref $initial_value
  59         1269  
196             ? $initial_value : \$initial_value;
197             }
198             else {
199 9         17 return if BROKEN_ISA_ASSIGNMENT && $name eq 'ISA';
200 9         31 *{ $namespace->{$name} } = _undef_ref_for_type($type);
  9         88  
201             }
202             }
203             }
204              
205             sub _undef_ref_for_type {
206 9     9   24 my ($type) = @_;
207              
208 9 100       44 if ($type eq 'ARRAY') {
    100          
    100          
    50          
    0          
209 3         7 return [];
210             }
211             elsif ($type eq 'HASH') {
212 2         7 return {};
213             }
214             elsif ($type eq 'SCALAR') {
215 3         9 return \undef;
216             }
217             elsif ($type eq 'IO') {
218 1         5 return Symbol::geniosym;
219             }
220             elsif ($type eq 'CODE') {
221 0         0 confess "Don't know how to vivify CODE variables";
222             }
223             else {
224 0         0 confess "Unknown type $type in vivication";
225             }
226             }
227              
228             sub remove_glob {
229 10     10 0 59 my ($self, $name) = @_;
230 10         24 delete $self->namespace->{$name};
231             }
232              
233             sub has_symbol {
234 124     124 0 14410 my ($self, $variable) = @_;
235              
236 124         257 my ($name, $sigil, $type) = _deconstruct_variable_name($variable);
237              
238 124         304 my $namespace = $self->namespace;
239              
240 124 100       308 return unless exists $namespace->{$name};
241              
242 120         211 my $entry_ref = \$namespace->{$name};
243 120 50       332 if (reftype($entry_ref) eq 'GLOB') {
244 120 100       239 if ($type eq 'SCALAR') {
245 31         46 if (BROKEN_SCALAR_INITIALIZATION) {
246             return defined ${ *{$entry_ref}{$type} };
247             }
248             else {
249 31         213 my $sv = B::svref_2object($entry_ref)->SV;
250 31   100     592 return $sv->isa('B::SV')
251             || ($sv->isa('B::SPECIAL')
252             && $B::specialsv_name[$$sv] ne 'Nullsv');
253             }
254             }
255             else {
256 89         122 return defined *{$entry_ref}{$type};
  89         439  
257             }
258             }
259             else {
260             # a symbol table entry can be -1 (stub), string (stub with prototype),
261             # or reference (constant)
262 0         0 return $type eq 'CODE';
263             }
264             }
265              
266             sub get_symbol {
267 147     147 0 26387 my ($self, $variable, %opts) = @_;
268              
269 147         326 my ($name, $sigil, $type) = _deconstruct_variable_name($variable);
270              
271 145         349 my $namespace = $self->namespace;
272              
273 145 100       368 if (!exists $namespace->{$name}) {
274 13 100       35 if ($opts{vivify}) {
275 7         35 $self->add_symbol($variable);
276             }
277             else {
278 6         40 return undef;
279             }
280             }
281              
282 139         263 my $entry_ref = \$namespace->{$name};
283              
284 139 100       336 if (ref($entry_ref) eq 'GLOB') {
285 138         194 return *{$entry_ref}{$type};
  138         3072  
286             }
287             else {
288 1 50       4 if ($type eq 'CODE') {
289 1 50       5 if (BROKEN_GLOB_ASSIGNMENT || defined($self->{package})) {
290 24     24   200 no strict 'refs';
  24         47  
  24         18697  
291 0         0 return \&{ $self->name . '::' . $name };
  0         0  
292             }
293              
294             # XXX we should really be able to support arbitrary anonymous
295             # stashes here... (not just via Package::Anon)
296 1 50 33     6 if (blessed($namespace) && $namespace->isa('Package::Anon')) {
297             # ->can will call gv_init for us, which inflates the glob
298             # don't know how to do this in general
299 0         0 $namespace->bless(\(my $foo))->can($name);
300             }
301             else {
302 1         206 confess "Don't know how to inflate a " . ref($entry_ref)
303             . " into a full coderef (perhaps you could use"
304             . " Package::Anon instead of a bare stash?)"
305             }
306              
307 0         0 return *{ $namespace->{$name} }{CODE};
  0         0  
308             }
309             else {
310 0         0 return undef;
311             }
312             }
313             }
314              
315             sub get_or_add_symbol {
316 16     16 0 3761 my $self = shift;
317 16         54 $self->get_symbol(@_, vivify => 1);
318             }
319              
320             sub remove_symbol {
321 10     10 1 3555 my ($self, $variable) = @_;
322              
323 10         31 my ($name, $sigil, $type) = _deconstruct_variable_name($variable);
324              
325             # FIXME:
326             # no doubt this is grossly inefficient and
327             # could be done much easier and faster in XS
328              
329 10         116 my %desc = (
330             SCALAR => { sigil => '$', type => 'SCALAR', name => $name },
331             ARRAY => { sigil => '@', type => 'ARRAY', name => $name },
332             HASH => { sigil => '%', type => 'HASH', name => $name },
333             CODE => { sigil => '&', type => 'CODE', name => $name },
334             IO => { sigil => '', type => 'IO', name => $name },
335             );
336 10 50       34 confess "This should never ever ever happen" if !$desc{$type};
337              
338 10 100       42 my @types_to_store = grep { $type ne $_ && $self->has_symbol($desc{$_}) }
  50         159  
339             keys %desc;
340 10         23 my %values = map { $_, $self->get_symbol($desc{$_}) } @types_to_store;
  20         47  
341              
342             $values{SCALAR} = $self->get_symbol($desc{SCALAR})
343             if !defined $values{SCALAR}
344 10 50 100     54 && $type ne 'SCALAR'
      100        
345             && BROKEN_SCALAR_INITIALIZATION;
346              
347 10         29 $self->remove_glob($name);
348              
349             $self->add_symbol($desc{$_} => $values{$_})
350 10         27 for grep { defined $values{$_} } keys %values;
  20         66  
351             }
352              
353             sub list_all_symbols {
354 22     22 0 1164 my ($self, $type_filter) = @_;
355              
356 22         46 my $namespace = $self->namespace;
357 22         29 if (HAS_ISA_CACHE) {
358             return grep { $_ ne '::ISA::CACHE::' } keys %{$namespace}
359             unless defined $type_filter;
360             }
361             else {
362 22 100       54 return keys %{$namespace}
  4         43  
363             unless defined $type_filter;
364             }
365              
366             # NOTE:
367             # or we can filter based on
368             # type (SCALAR|ARRAY|HASH|CODE)
369 18 100       60 if ($type_filter eq 'CODE') {
    100          
370             return grep {
371             # any non-typeglob in the symbol table is a constant or stub
372             ref(\$namespace->{$_}) ne 'GLOB'
373             # regular subs are stored in the CODE slot of the typeglob
374 53         245 || defined(*{$namespace->{$_}}{CODE})
375 7 50       11 } keys %{$namespace};
  53         123  
  7         28  
376             }
377             elsif ($type_filter eq 'SCALAR') {
378             return grep {
379             !(HAS_ISA_CACHE && $_ eq '::ISA::CACHE::') &&
380             (BROKEN_SCALAR_INITIALIZATION
381             ? (ref(\$namespace->{$_}) eq 'GLOB'
382             && defined(${*{$namespace->{$_}}{'SCALAR'}}))
383 17         31 : (do {
384 17         27 my $entry = \$namespace->{$_};
385 17 50       172 ref($entry) eq 'GLOB'
386             && B::svref_2object($entry)->SV->isa('B::SV')
387             }))
388 3         7 } keys %{$namespace};
  3         375  
389             }
390             else {
391             return grep {
392             ref(\$namespace->{$_}) eq 'GLOB'
393 60 50       134 && defined(*{$namespace->{$_}}{$type_filter})
  60         262  
394 8         13 } keys %{$namespace};
  8         25  
395             }
396             }
397              
398             sub get_all_symbols {
399 6     6 0 1885 my ($self, $type_filter) = @_;
400              
401 6         16 my $namespace = $self->namespace;
402 6 100       18 return { %{$namespace} } unless defined $type_filter;
  2         24  
403              
404             return {
405 4         13 map { $_ => $self->get_symbol({name => $_, type => $type_filter}) }
  8         31  
406             $self->list_all_symbols($type_filter)
407             }
408             }
409              
410              
411             1;
412              
413             __END__