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