Alright, it is ugly, but it gets the job done. This handles everything but var pragma variables that have never been assigned to (they don't really exist yet). This version also has the benefit of displaying package and lexical variables differently, so you can easily spot the masking effect. Hmm, but I think there might be a bug in the case where you have
{
our $foo;
{
my $foo;
{
our $foo;
print dumper(\$foo);
}
}
}
But I am not sure anything can be done about that.
#!/usr/bin/perl
use strict;
use warnings;
use PadWalker qw/peek_our peek_my/;
use Scalar::Util 'refaddr';
use Data::Dumper;
use vars qw/$baz $quux/;
$other::package::fork = 0;
our $foo = 1;
our $bar = 2;
$baz = 3;
print "now all are package\n", dumper(
\$foo,
\$bar,
\$baz,
\$quux,
\$",
);
{
my $foo = 4;
print "\nnow \$foo is masked by a lexical\n", dumper(
\$foo,
\$bar,
\$baz,
\$quux,
\$",
);
}
sub find_package_vars {
my ($package, $pad_vars) = @_;
no strict 'refs';
no warnings;
foreach my $name (%{$package}) {
my $pkgname = "${package}$name";
if ($name =~ /::/) {
next if $name =~ /^\*?main::$/;
find_package_vars($pkgname, $pad_vars);
next;
}
my $glob = ${$package}{$name};
if (defined ${$glob}) {
$pad_vars->{ refaddr \${$glob} } = "\$$pkgname";
}
#FIXME: there is probably a better way, but I am lazy at the moment
eval {
if (defined @{$glob}) {
$pad_vars->{ refaddr \@{$glob} } = $pkgname;
}
};
eval {
if (defined %{$glob}) {
$pad_vars->{ refaddr \%{$glob} } = $pkgname;
}
};
}
}
sub dumper {
my %pad_vars;
find_package_vars("main::", \%pad_vars);
my $lexical = peek_my(1);
while ( my ( $var, $ref ) = each %$lexical ) {
# we no longer remove the '$' sigil because we don't want
# "$foo = \@array" reported as "@foo".
$var =~ s/^[\@\%]/*/;
$pad_vars{ refaddr $ref } = $var;
}
my @names;
my $varcount = 1;
foreach (@_) {
my $name;
INNER: foreach ( \$_, $_ ) {
no warnings 'uninitialized';
$name = $pad_vars{ refaddr $_} and last INNER;
}
push @names, $name;
}
return Data::Dumper->Dump( \@_, \@names );
}