regexprefs are valid scalar refs too
[gitmo/Package-Stash.git] / lib / Package / Stash / PP.pm
CommitLineData
2905fb35 1package Package::Stash::PP;
f10f6217 2use strict;
3use warnings;
2905fb35 4# ABSTRACT: pure perl implementation of the Package::Stash API
f10f6217 5
6use Carp qw(confess);
eb53b1bd 7use Scalar::Util qw(blessed reftype weaken);
dc378b60 8use Symbol;
9# before 5.12, assigning to the ISA glob would make it lose its magical ->isa
10# powers
11use constant BROKEN_ISA_ASSIGNMENT => ($] < 5.012);
36cbfbad 12# before 5.10, stashes don't ever seem to drop to a refcount of zero, so
13# weakening them isn't helpful
14use constant BROKEN_WEAK_STASH => ($] < 5.010);
f4979588 15
f4979588 16=head1 SYNOPSIS
17
2905fb35 18 use Package::Stash;
f4979588 19
20=head1 DESCRIPTION
21
2905fb35 22This is a backend for L<Package::Stash> implemented in pure perl, for those without a compiler or who would like to use this inline in scripts.
f4979588 23
24=cut
25
f10f6217 26sub new {
27 my $class = shift;
e55970bc 28 my ($package) = @_;
774f4f10 29
30 if (!defined($package) || (ref($package) && ref($package) ne 'HASH')) {
31 confess "Package::Stash->new must be passed the name of the "
32 . "package to access";
33 }
34 elsif (ref($package) eq 'HASH') {
35 confess "The pure perl implementation of Package::Stash doesn't "
36 . "currently support anonymous stashes. You should install "
37 . "Package::Stash::XS";
38 }
39
e55970bc 40 return bless {
eb53b1bd 41 'package' => $package,
e55970bc 42 }, $class;
f10f6217 43}
44
45sub name {
2905fb35 46 confess "Can't call name as a class method"
47 unless blessed($_[0]);
f10f6217 48 return $_[0]->{package};
49}
50
51sub namespace {
2905fb35 52 confess "Can't call namespace as a class method"
53 unless blessed($_[0]);
eb53b1bd 54
36cbfbad 55 if (BROKEN_WEAK_STASH) {
eb53b1bd 56 no strict 'refs';
36cbfbad 57 return \%{$_[0]->name . '::'};
eb53b1bd 58 }
36cbfbad 59 else {
60 return $_[0]->{namespace} if defined $_[0]->{namespace};
eb53b1bd 61
36cbfbad 62 {
63 no strict 'refs';
64 $_[0]->{namespace} = \%{$_[0]->name . '::'};
65 }
66
67 weaken($_[0]->{namespace});
eb53b1bd 68
36cbfbad 69 return $_[0]->{namespace};
70 }
f10f6217 71}
72
73{
74 my %SIGIL_MAP = (
75 '$' => 'SCALAR',
76 '@' => 'ARRAY',
77 '%' => 'HASH',
78 '&' => 'CODE',
56a29840 79 '' => 'IO',
f10f6217 80 );
81
82 sub _deconstruct_variable_name {
83 my ($self, $variable) = @_;
84
56a29840 85 (defined $variable && length $variable)
f10f6217 86 || confess "You must pass a variable name";
87
88 my $sigil = substr($variable, 0, 1, '');
89
56a29840 90 if (exists $SIGIL_MAP{$sigil}) {
91 return ($variable, $sigil, $SIGIL_MAP{$sigil});
92 }
93 else {
94 return ("${sigil}${variable}", '', $SIGIL_MAP{''});
95 }
f10f6217 96 }
97}
98
3634ce60 99sub _valid_for_type {
100 my $self = shift;
101 my ($value, $type) = @_;
102 if ($type eq 'HASH' || $type eq 'ARRAY'
103 || $type eq 'IO' || $type eq 'CODE') {
104 return reftype($value) eq $type;
105 }
106 else {
107 my $ref = reftype($value);
bca98651 108 return !defined($ref) || $ref eq 'SCALAR' || $ref eq 'REF' || $ref eq 'LVALUE' || $ref eq 'REGEXP';
3634ce60 109 }
110}
111
2905fb35 112sub add_symbol {
640de369 113 my ($self, $variable, $initial_value, %opts) = @_;
f10f6217 114
115 my ($name, $sigil, $type) = ref $variable eq 'HASH'
116 ? @{$variable}{qw[name sigil type]}
117 : $self->_deconstruct_variable_name($variable);
118
4ada57e0 119 my $pkg = $self->name;
120
3634ce60 121 if (@_ > 2) {
122 $self->_valid_for_type($initial_value, $type)
123 || confess "$initial_value is not of type $type";
3634ce60 124
4ada57e0 125 # cheap fail-fast check for PERLDBf_SUBLINE and '&'
126 if ($^P and $^P & 0x10 && $sigil eq '&') {
640de369 127 my $filename = $opts{filename};
128 my $first_line_num = $opts{first_line_num};
4ada57e0 129
640de369 130 (undef, $filename, $first_line_num) = caller
4ada57e0 131 if not defined $filename;
640de369 132
133 my $last_line_num = $opts{last_line_num} || ($first_line_num ||= 0);
4ada57e0 134
135 # http://perldoc.perl.org/perldebguts.html#Debugger-Internals
640de369 136 $DB::sub{$pkg . '::' . $name} = "$filename:$first_line_num-$last_line_num";
4ada57e0 137 }
138 }
f10f6217 139
140 no strict 'refs';
141 no warnings 'redefine', 'misc', 'prototype';
142 *{$pkg . '::' . $name} = ref $initial_value ? $initial_value : \$initial_value;
143}
144
2905fb35 145sub remove_glob {
f10f6217 146 my ($self, $name) = @_;
65b8253b 147 delete $self->namespace->{$name};
f10f6217 148}
149
2905fb35 150sub has_symbol {
f10f6217 151 my ($self, $variable) = @_;
152
153 my ($name, $sigil, $type) = ref $variable eq 'HASH'
154 ? @{$variable}{qw[name sigil type]}
155 : $self->_deconstruct_variable_name($variable);
156
157 my $namespace = $self->namespace;
158
159 return unless exists $namespace->{$name};
160
161 my $entry_ref = \$namespace->{$name};
162 if (reftype($entry_ref) eq 'GLOB') {
f7543739 163 # XXX: assigning to any typeglob slot also initializes the SCALAR slot,
164 # and saying that an undef scalar variable doesn't exist is probably
165 # vaguely less surprising than a scalar variable popping into existence
166 # without anyone defining it
167 if ($type eq 'SCALAR') {
168 return defined ${ *{$entry_ref}{$type} };
169 }
170 else {
171 return defined *{$entry_ref}{$type};
172 }
f10f6217 173 }
174 else {
175 # a symbol table entry can be -1 (stub), string (stub with prototype),
176 # or reference (constant)
177 return $type eq 'CODE';
178 }
179}
180
2905fb35 181sub get_symbol {
e55803fc 182 my ($self, $variable, %opts) = @_;
f10f6217 183
184 my ($name, $sigil, $type) = ref $variable eq 'HASH'
185 ? @{$variable}{qw[name sigil type]}
186 : $self->_deconstruct_variable_name($variable);
187
188 my $namespace = $self->namespace;
189
7486ccf3 190 if (!exists $namespace->{$name}) {
dc378b60 191 if ($opts{vivify}) {
192 if ($type eq 'ARRAY') {
193 if (BROKEN_ISA_ASSIGNMENT) {
2905fb35 194 $self->add_symbol(
dc378b60 195 $variable,
196 $name eq 'ISA' ? () : ([])
197 );
198 }
199 else {
2905fb35 200 $self->add_symbol($variable, []);
dc378b60 201 }
202 }
203 elsif ($type eq 'HASH') {
2905fb35 204 $self->add_symbol($variable, {});
dc378b60 205 }
206 elsif ($type eq 'SCALAR') {
2905fb35 207 $self->add_symbol($variable);
dc378b60 208 }
209 elsif ($type eq 'IO') {
2905fb35 210 $self->add_symbol($variable, Symbol::geniosym);
dc378b60 211 }
212 elsif ($type eq 'CODE') {
213 confess "Don't know how to vivify CODE variables";
214 }
215 else {
216 confess "Unknown type $type in vivication";
217 }
5d3589c8 218 }
e55803fc 219 else {
4723417a 220 return undef;
e55803fc 221 }
30d1a098 222 }
f10f6217 223
224 my $entry_ref = \$namespace->{$name};
225
226 if (ref($entry_ref) eq 'GLOB') {
227 return *{$entry_ref}{$type};
228 }
229 else {
230 if ($type eq 'CODE') {
231 no strict 'refs';
232 return \&{ $self->name . '::' . $name };
233 }
234 else {
235 return undef;
236 }
237 }
238}
239
2905fb35 240sub get_or_add_symbol {
e55803fc 241 my $self = shift;
2905fb35 242 $self->get_symbol(@_, vivify => 1);
e55803fc 243}
244
2905fb35 245sub remove_symbol {
f10f6217 246 my ($self, $variable) = @_;
247
248 my ($name, $sigil, $type) = ref $variable eq 'HASH'
249 ? @{$variable}{qw[name sigil type]}
250 : $self->_deconstruct_variable_name($variable);
251
252 # FIXME:
9a3d1390 253 # no doubt this is grossly inefficient and
f10f6217 254 # could be done much easier and faster in XS
255
b1a00d0e 256 my ($scalar_desc, $array_desc, $hash_desc, $code_desc, $io_desc) = (
f10f6217 257 { sigil => '$', type => 'SCALAR', name => $name },
258 { sigil => '@', type => 'ARRAY', name => $name },
259 { sigil => '%', type => 'HASH', name => $name },
260 { sigil => '&', type => 'CODE', name => $name },
b1a00d0e 261 { sigil => '', type => 'IO', name => $name },
f10f6217 262 );
263
b1a00d0e 264 my ($scalar, $array, $hash, $code, $io);
f10f6217 265 if ($type eq 'SCALAR') {
2905fb35 266 $array = $self->get_symbol($array_desc) if $self->has_symbol($array_desc);
267 $hash = $self->get_symbol($hash_desc) if $self->has_symbol($hash_desc);
268 $code = $self->get_symbol($code_desc) if $self->has_symbol($code_desc);
269 $io = $self->get_symbol($io_desc) if $self->has_symbol($io_desc);
f10f6217 270 }
271 elsif ($type eq 'ARRAY') {
2905fb35 272 $scalar = $self->get_symbol($scalar_desc);
273 $hash = $self->get_symbol($hash_desc) if $self->has_symbol($hash_desc);
274 $code = $self->get_symbol($code_desc) if $self->has_symbol($code_desc);
275 $io = $self->get_symbol($io_desc) if $self->has_symbol($io_desc);
f10f6217 276 }
277 elsif ($type eq 'HASH') {
2905fb35 278 $scalar = $self->get_symbol($scalar_desc);
279 $array = $self->get_symbol($array_desc) if $self->has_symbol($array_desc);
280 $code = $self->get_symbol($code_desc) if $self->has_symbol($code_desc);
281 $io = $self->get_symbol($io_desc) if $self->has_symbol($io_desc);
f10f6217 282 }
283 elsif ($type eq 'CODE') {
2905fb35 284 $scalar = $self->get_symbol($scalar_desc);
285 $array = $self->get_symbol($array_desc) if $self->has_symbol($array_desc);
286 $hash = $self->get_symbol($hash_desc) if $self->has_symbol($hash_desc);
287 $io = $self->get_symbol($io_desc) if $self->has_symbol($io_desc);
b1a00d0e 288 }
289 elsif ($type eq 'IO') {
2905fb35 290 $scalar = $self->get_symbol($scalar_desc);
291 $array = $self->get_symbol($array_desc) if $self->has_symbol($array_desc);
292 $hash = $self->get_symbol($hash_desc) if $self->has_symbol($hash_desc);
293 $code = $self->get_symbol($code_desc) if $self->has_symbol($code_desc);
f10f6217 294 }
295 else {
296 confess "This should never ever ever happen";
297 }
298
2905fb35 299 $self->remove_glob($name);
f10f6217 300
2905fb35 301 $self->add_symbol($scalar_desc => $scalar);
302 $self->add_symbol($array_desc => $array) if defined $array;
303 $self->add_symbol($hash_desc => $hash) if defined $hash;
304 $self->add_symbol($code_desc => $code) if defined $code;
305 $self->add_symbol($io_desc => $io) if defined $io;
f10f6217 306}
307
2905fb35 308sub list_all_symbols {
f10f6217 309 my ($self, $type_filter) = @_;
310
311 my $namespace = $self->namespace;
312 return keys %{$namespace} unless defined $type_filter;
313
314 # NOTE:
9a3d1390 315 # or we can filter based on
f10f6217 316 # type (SCALAR|ARRAY|HASH|CODE)
317 if ($type_filter eq 'CODE') {
318 return grep {
25c87f5c 319 # any non-typeglob in the symbol table is a constant or stub
320 ref(\$namespace->{$_}) ne 'GLOB'
321 # regular subs are stored in the CODE slot of the typeglob
d1f721b3 322 || defined(*{$namespace->{$_}}{CODE})
323 } keys %{$namespace};
324 }
325 elsif ($type_filter eq 'SCALAR') {
326 return grep {
327 ref(\$namespace->{$_}) eq 'GLOB'
328 && defined(${*{$namespace->{$_}}{'SCALAR'}})
329 } keys %{$namespace};
330 }
331 else {
332 return grep {
333 ref(\$namespace->{$_}) eq 'GLOB'
334 && defined(*{$namespace->{$_}}{$type_filter})
f10f6217 335 } keys %{$namespace};
f10f6217 336 }
337}
f4979588 338
2905fb35 339sub get_all_symbols {
340 my ($self, $type_filter) = @_;
341
342 my $namespace = $self->namespace;
343 return { %{$namespace} } unless defined $type_filter;
344
345 return {
346 map { $_ => $self->get_symbol({name => $_, type => $type_filter}) }
347 $self->list_all_symbols($type_filter)
348 }
349}
350
0992f4ec 351=head1 BUGS
352
2905fb35 353=over 4
354
355=item * Scalar slots are only considered to exist if they are defined
356
357This is due to a shortcoming within perl itself. See
358L<perlref/Making References> point 7 for more information.
359
360=item * remove_symbol also replaces the associated typeglob
361
362This can cause unexpected behavior when doing manipulation at compile time -
363removing subroutines will still allow them to be called from within the package
364as subroutines (although they will not be available as methods). This can be
365considered a feature in some cases (this is how L<namespace::clean> works, for
366instance), but should not be relied upon - use C<remove_glob> directly if you
367want this behavior.
368
cdb543b5 369=item * Some minor memory leaks
370
371The pure perl implementation has a couple minor memory leaks (see the TODO
372tests in t/20-leaks.t) that I'm having a hard time tracking down - these may be
373core perl bugs, it's hard to tell.
374
2905fb35 375=back
0992f4ec 376
377Please report any bugs through RT: email
378C<bug-package-stash at rt.cpan.org>, or browse to
379L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Package-Stash>.
380
f4979588 381=head1 SEE ALSO
382
f4979588 383=over 4
384
988beb41 385=item * L<Class::MOP::Package>
f4979588 386
988beb41 387This module is a factoring out of code that used to live here
f4979588 388
389=back
390
0992f4ec 391=head1 SUPPORT
392
393You can find this documentation for this module with the perldoc command.
394
395 perldoc Package::Stash
396
397You can also look for information at:
398
399=over 4
400
401=item * AnnoCPAN: Annotated CPAN documentation
402
403L<http://annocpan.org/dist/Package-Stash>
404
405=item * CPAN Ratings
406
407L<http://cpanratings.perl.org/d/Package-Stash>
408
409=item * RT: CPAN's request tracker
410
411L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Package-Stash>
412
413=item * Search CPAN
414
415L<http://search.cpan.org/dist/Package-Stash>
416
417=back
418
419=head1 AUTHOR
420
421Jesse Luehrs <doy at tozt dot net>
422
423Mostly copied from code from L<Class::MOP::Package>, by Stevan Little and the
424Moose Cabal.
425
a912fc4b 426=begin Pod::Coverage
427
428BROKEN_ISA_ASSIGNMENT
429add_symbol
430get_all_symbols
431get_or_add_symbol
432get_symbol
433has_symbol
434list_all_symbols
435name
436namespace
437new
438remove_glob
439
440=end Pod::Coverage
441
f4979588 442=cut
443
4441;