Heavy.pm revision 7c478bd95313f5f23a4c958a745db2134aa03244
package Exporter;
=head1 NAME
Exporter::Heavy - Exporter guts
=head1 SYNOPIS
(internal use only)
=head1 DESCRIPTION
No user-serviceable parts inside.
=cut
#
# We go to a lot of trouble not to 'require Carp' at file scope,
# because Carp requires Exporter, and something has to give.
#
sub heavy_export {
# First make import warnings look like they're coming from the "use".
local $SIG{__WARN__} = sub {
my $text = shift;
if ($text =~ s/ at \S*Exporter\S*.pm line \d+.*\n//) {
require Carp;
local $Carp::CarpLevel = 1; # ignore package calling us too.
Carp::carp($text);
}
else {
warn $text;
}
};
local $SIG{__DIE__} = sub {
require Carp;
local $Carp::CarpLevel = 1; # ignore package calling us too.
Carp::croak("$_[0]Illegal null symbol in \@${1}::EXPORT")
if $_[0] =~ /^Unable to create sub named "(.*?)::"/;
};
my($pkg, $callpkg, @imports) = @_;
my($type, $sym, $oops);
*exports = *{"${pkg}::EXPORT"};
if (@imports) {
if (!%exports) {
grep(s/^&//, @exports);
@exports{@exports} = (1) x @exports;
my $ok = \@{"${pkg}::EXPORT_OK"};
if (@$ok) {
grep(s/^&//, @$ok);
@exports{@$ok} = (1) x @$ok;
}
}
if ($imports[0] =~ m#^[/!:]#){
my $tagsref = \%{"${pkg}::EXPORT_TAGS"};
my $tagdata;
my %imports;
my($remove, $spec, @names, @allexports);
# negated first item implies starting with default set:
unshift @imports, ':DEFAULT' if $imports[0] =~ m/^!/;
foreach $spec (@imports){
$remove = $spec =~ s/^!//;
if ($spec =~ s/^://){
if ($spec eq 'DEFAULT'){
@names = @exports;
}
elsif ($tagdata = $tagsref->{$spec}) {
@names = @$tagdata;
}
else {
warn qq["$spec" is not defined in %${pkg}::EXPORT_TAGS];
++$oops;
next;
}
}
elsif ($spec =~ m:^/(.*)/$:){
my $patn = $1;
@allexports = keys %exports unless @allexports; # only do keys once
@names = grep(/$patn/, @allexports); # not anchored by default
}
else {
@names = ($spec); # is a normal symbol name
}
warn "Import ".($remove ? "del":"add").": @names "
if $Verbose;
if ($remove) {
foreach $sym (@names) { delete $imports{$sym} }
}
else {
@imports{@names} = (1) x @names;
}
}
@imports = keys %imports;
}
foreach $sym (@imports) {
if (!$exports{$sym}) {
if ($sym =~ m/^\d/) {
$pkg->require_version($sym);
# If the version number was the only thing specified
# then we should act as if nothing was specified:
if (@imports == 1) {
@imports = @exports;
last;
}
# We need a way to emulate 'use Foo ()' but still
# allow an easy version check: "use Foo 1.23, ''";
if (@imports == 2 and !$imports[1]) {
@imports = ();
last;
}
} elsif ($sym !~ s/^&// || !$exports{$sym}) {
require Carp;
Carp::carp(qq["$sym" is not exported by the $pkg module]);
$oops++;
}
}
}
if ($oops) {
require Carp;
Carp::croak("Can't continue after import errors");
}
}
else {
@imports = @exports;
}
*fail = *{"${pkg}::EXPORT_FAIL"};
if (@fail) {
if (!%fail) {
# Build cache of symbols. Optimise the lookup by adding
# barewords twice... both with and without a leading &.
# (Technique could be applied to %exports cache at cost of memory)
my @expanded = map { /^\w/ ? ($_, '&'.$_) : $_ } @fail;
warn "${pkg}::EXPORT_FAIL cached: @expanded" if $Verbose;
@fail{@expanded} = (1) x @expanded;
}
my @failed;
foreach $sym (@imports) { push(@failed, $sym) if $fail{$sym} }
if (@failed) {
@failed = $pkg->export_fail(@failed);
foreach $sym (@failed) {
require Carp;
Carp::carp(qq["$sym" is not implemented by the $pkg module ],
"on this architecture");
}
if (@failed) {
require Carp;
Carp::croak("Can't continue after import errors");
}
}
}
warn "Importing into $callpkg from $pkg: ",
join(", ",sort @imports) if $Verbose;
foreach $sym (@imports) {
# shortcut for the common case of no type character
(*{"${callpkg}::$sym"} = \&{"${pkg}::$sym"}, next)
unless $sym =~ s/^(\W)//;
$type = $1;
*{"${callpkg}::$sym"} =
$type eq '&' ? \&{"${pkg}::$sym"} :
$type eq '$' ? \${"${pkg}::$sym"} :
$type eq '@' ? \@{"${pkg}::$sym"} :
$type eq '%' ? \%{"${pkg}::$sym"} :
$type eq '*' ? *{"${pkg}::$sym"} :
do { require Carp; Carp::croak("Can't export symbol: $type$sym") };
}
}
sub heavy_export_to_level
{
my $pkg = shift;
my $level = shift;
(undef) = shift; # XXX redundant arg
my $callpkg = caller($level);
$pkg->export($callpkg, @_);
}
# Utility functions
sub _push_tags {
my($pkg, $var, $syms) = @_;
my $nontag;
*export_tags = \%{"${pkg}::EXPORT_TAGS"};
push(@{"${pkg}::$var"},
map { $export_tags{$_} ? @{$export_tags{$_}} : scalar(++$nontag,$_) }
(@$syms) ? @$syms : keys %export_tags);
if ($nontag and $^W) {
# This may change to a die one day
require Carp;
Carp::carp("Some names are not tags");
}
}
# Default methods
sub export_fail {
my $self = shift;
@_;
}
sub require_version {
my($self, $wanted) = @_;
my $pkg = ref $self || $self;
my $version = ${"${pkg}::VERSION"};
if (!$version or $version < $wanted) {
$version ||= "(undef)";
# %INC contains slashes, but $pkg contains double-colons.
my $file = (map {s,::,/,g; $INC{$_}} "$pkg.pm")[0];
$file &&= " ($file)";
require Carp;
Carp::croak("$pkg $wanted required--this is only version $version$file")
}
$version;
}
1;