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