| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | =head1 NAME | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | Exporter::VA - Improved Exporter featuring Versioning and Aliasing. | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | =cut | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | ### see the main POD at the end of this file. | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | package Exporter::VA; | 
| 10 | 1 |  |  | 1 |  | 24959 | use strict; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 39 |  | 
| 11 | 1 |  |  | 1 |  | 5 | use warnings; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 35 |  | 
| 12 | 1 |  |  | 1 |  | 5 | use Carp qw/croak carp/; | 
|  | 1 |  |  |  |  | 6 |  | 
|  | 1 |  |  |  |  | 69 |  | 
| 13 | 1 |  |  | 1 |  | 888 | use utf8; | 
|  | 1 |  |  |  |  | 9 |  | 
|  | 1 |  |  |  |  | 6 |  | 
| 14 |  |  |  |  |  |  | our $VERSION= v1.3.0.1;  # major.minor.update.docsonly | 
| 15 |  |  |  |  |  |  | *VERBOSE= *STDERR{IO};   # can be redirected | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | my %EXPORT= ( | 
| 18 |  |  |  |  |  |  | '&VERSION' => \&export_VERSION, | 
| 19 |  |  |  |  |  |  | '&import' => \&export_import, | 
| 20 |  |  |  |  |  |  | '&AUTOLOAD' => \&export_AUTOLOAD, | 
| 21 |  |  |  |  |  |  | '.default_VERSION'=> v0.1, | 
| 22 |  |  |  |  |  |  | ':normal' => [qw/ &VERSION &import &AUTOLOAD/ ], | 
| 23 |  |  |  |  |  |  | '.&begin' => \&begin, | 
| 24 |  |  |  |  |  |  | '&normalize_vstring' => \\&normalize_vstring, | 
| 25 |  |  |  |  |  |  | '&autoload_symbol' => \\&autoload_symbol, | 
| 26 |  |  |  |  |  |  | ); | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | sub Err | 
| 29 |  |  |  |  |  |  | { | 
| 30 |  |  |  |  |  |  | # improve this to give proper level information to Croak. | 
| 31 | 2 |  |  | 2 | 0 | 363 | croak @_; | 
| 32 |  |  |  |  |  |  | } | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | sub Warn | 
| 35 |  |  |  |  |  |  | { | 
| 36 | 0 |  |  | 0 | 0 | 0 | carp @_; | 
| 37 |  |  |  |  |  |  | } | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | sub dump | 
| 40 |  |  |  |  |  |  | { | 
| 41 |  |  |  |  |  |  | # Currently implemented to use Data::Dumper, but might change to be more custom some day. | 
| 42 | 1 |  |  | 1 | 1 | 2 | eval {  require Data::Dumper }; | 
|  | 1 |  |  |  |  | 1070 |  | 
| 43 | 1 | 50 |  |  |  | 8170 | if ($@) { | 
| 44 | 0 |  |  |  |  | 0 | print VERBOSE "**(Exporter::VA::dump) ERROR: cannot load Data::Dumper module to support the dump() method or --dump pragma\n"; | 
| 45 | 0 |  |  |  |  | 0 | return; | 
| 46 |  |  |  |  |  |  | } | 
| 47 | 1 |  |  |  |  | 3 | my $self= shift; | 
| 48 | 1 |  |  |  |  | 11 | print VERBOSE (Data::Dumper->Dump ( [ $self ], ["*EXPORT"]), $/); | 
| 49 |  |  |  |  |  |  | } | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | sub is_vstring($) | 
| 52 |  |  |  |  |  |  | { | 
| 53 | 45 |  |  | 45 | 0 | 77 | my $s= shift; | 
| 54 | 45 |  |  |  |  | 2252 | my $count= $s =~ tr/\0-\1F//; | 
| 55 | 45 |  |  |  |  | 187 | return $count > 0; | 
| 56 |  |  |  |  |  |  | # to disambiguate a v-string like v65.66.67, add a trailing .0 becoming v.65.66.67.0 with same meaning. | 
| 57 |  |  |  |  |  |  | } | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | sub normalize_vstring ($) | 
| 60 |  |  |  |  |  |  | { | 
| 61 | 40 |  |  | 40 | 1 | 7956 | my $v= shift; | 
| 62 |  |  |  |  |  |  | # for now, doesn't do much. | 
| 63 | 40 | 100 |  |  |  | 120 | return v0 if length ($v) eq 0; | 
| 64 | 39 | 100 |  |  |  | 68 | $v= pack ("U*", split (/\./,$v)) | 
| 65 |  |  |  |  |  |  | unless is_vstring ($v); | 
| 66 |  |  |  |  |  |  | # remove trailing redundant zeros (but keep it at least 2 digits, so v1.0 is right, v1.0.0.0 is truncated) | 
| 67 | 39 |  |  |  |  | 117 | $v =~ s/(?<=..)\0+$//; | 
| 68 | 39 |  |  |  |  | 150 | return $v; | 
| 69 |  |  |  |  |  |  | } | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | sub _calling_client() | 
| 73 |  |  |  |  |  |  | { | 
| 74 | 24 |  |  | 24 |  | 32 | my $n= 1; | 
| 75 | 24 |  |  |  |  | 31 | for (;;) { | 
| 76 | 28 |  |  |  |  | 46 | my $caller= caller($n); | 
| 77 | 28 | 100 |  |  |  | 92 | return $caller  if $caller ne __PACKAGE__;  # I want the first caller of this module | 
| 78 | 4 |  |  |  |  | 6 | ++$n; | 
| 79 |  |  |  |  |  |  | } | 
| 80 |  |  |  |  |  |  | } | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | sub _check_allowed_versions | 
| 83 |  |  |  |  |  |  | { | 
| 84 | 6 |  |  | 6 |  | 18 | my ($version, $list)= @_; | 
| 85 | 6 | 100 |  |  |  | 23 | return unless defined $list;  # if .allowed_VERSIONS is not specified, anything is allowed. | 
| 86 | 2 |  |  |  |  | 7 | foreach (@$list) { | 
| 87 | 3 | 100 |  |  |  | 13 | return  if $version eq $_;  # normalized earlier. | 
| 88 |  |  |  |  |  |  | } | 
| 89 |  |  |  |  |  |  | # compose error message | 
| 90 | 1 |  |  |  |  | 4 | my $vs= join( ', ', map { _format_vstring($_)} (@$list) ); | 
|  | 2 |  |  |  |  | 5 |  | 
| 91 | 1 |  |  |  |  | 4 | Err "(Exporter::VA) you asked for ", _format_vstring($version), ", but the only allowed versions are $vs"; | 
| 92 |  |  |  |  |  |  | } | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | sub generate_VERSION | 
| 95 |  |  |  |  |  |  | { | 
| 96 | 4 |  |  | 4 | 0 | 4 | my $export_def= shift;  # might not have been blessed yet. | 
| 97 |  |  |  |  |  |  | return sub {  # this becomes the VERSION function in the exporting module. | 
| 98 | 12 |  |  | 12 |  | 808 | my ($home, $version, $client)= @_; | 
| 99 | 12 | 100 |  |  |  | 41 | $client= _calling_client() unless defined $client;  # allow as optional argument | 
| 100 | 12 | 100 |  |  |  | 29 | if (defined $version) { | 
| 101 |  |  |  |  |  |  | # assure correct version / set desired version | 
| 102 | 6 |  |  |  |  | 15 | $version= normalize_vstring($version); | 
| 103 | 6 | 50 |  |  |  | 24 | Err "The version for this module has already been specified for module $client as ", _format_vstring ($export_def->{'..client_default_version'}{$client}) | 
| 104 |  |  |  |  |  |  | if exists $export_def->{'..client_default_version'}{$client}; | 
| 105 | 6 |  | 66 |  |  | 23 | my $max_version= $export_def->{'..max_VERSION'} || _get_VERSION ($home);  # first time, called before setup. | 
| 106 | 6 | 50 |  |  |  | 20 | Err "$client requested version ", _format_vstring ($version), " but module $home is only version ", _format_vstring ($max_version) | 
| 107 |  |  |  |  |  |  | if $version gt $max_version; | 
| 108 | 6 |  |  |  |  | 24 | _check_allowed_versions ($version, $export_def->{'.allowed_VERSIONS'}); | 
| 109 | 5 |  |  |  |  | 25 | $export_def->{'..client_default_version'}{$client}= $version; | 
| 110 |  |  |  |  |  |  | } | 
| 111 |  |  |  |  |  |  | else { | 
| 112 |  |  |  |  |  |  | # fetch version | 
| 113 | 6 | 100 |  |  |  | 58 | return $export_def->{'..client_default_version'}{$client}  if exists $export_def->{'..client_default_version'}{$client}; | 
| 114 |  |  |  |  |  |  | # never explicitly specified, so use the module's actual current version. | 
| 115 | 1 |  | 33 |  |  | 5 | $version= $export_def->{'..max_VERSION'} || _get_VERSION ($home); | 
| 116 | 1 |  |  |  |  | 3 | $export_def->{'..client_default_version'}{$client}= $version;  # once I decide, must always use the same result. | 
| 117 | 1 |  |  |  |  | 4 | return $version; | 
| 118 |  |  |  |  |  |  | } | 
| 119 |  |  |  |  |  |  | } | 
| 120 | 4 |  |  |  |  | 30 | } | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | sub get_import_version | 
| 123 |  |  |  |  |  |  | { | 
| 124 | 13 |  |  | 13 | 0 | 18 | my ($self, $client)= @_; | 
| 125 | 13 | 100 |  |  |  | 36 | unless (exists ($self->{'..client_default_version'}{$client})) { | 
| 126 | 8 | 50 |  |  |  | 22 | Err "(Exporter::VA) you must specify a version to import, since the module has no default." | 
| 127 |  |  |  |  |  |  | unless exists $$self{'.default_VERSION'}; | 
| 128 | 8 |  |  |  |  | 30 | $self->{'..client_default_version'}{$client}= $$self{'.default_VERSION'}; | 
| 129 | 8 | 50 |  |  |  | 20 | print VERBOSE "(Exporter::VA) import version not specified, using .default_VERSION\n" | 
| 130 |  |  |  |  |  |  | if $$self{'.verbose_import'}; | 
| 131 |  |  |  |  |  |  | } | 
| 132 | 13 |  |  |  |  | 39 | return $$self{'..client_default_version'}{$client}; | 
| 133 |  |  |  |  |  |  | } | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | sub _format_vstring($) | 
| 136 |  |  |  |  |  |  | { | 
| 137 | 9 |  |  | 9 |  | 50 | return sprintf ("v%vd", shift); | 
| 138 |  |  |  |  |  |  | } | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | sub _normalize_vstring_list | 
| 142 |  |  |  |  |  |  | { | 
| 143 | 2 |  |  | 2 |  | 4 | my $list= shift; | 
| 144 | 2 |  |  |  |  | 7 | for (my $loop= 0; $loop < @$list; $loop+=2) { | 
| 145 | 5 |  |  |  |  | 10 | normalize_vstring ($$list[$loop]); | 
| 146 |  |  |  |  |  |  | } | 
| 147 | 2 |  |  |  |  | 8 | bless $list, "ARRAY-seen"; | 
| 148 |  |  |  |  |  |  | } | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | sub _match_vstring_list | 
| 151 |  |  |  |  |  |  | { | 
| 152 | 7 |  |  | 7 |  | 17 | my ($list, $desired_version)= @_; | 
| 153 |  |  |  |  |  |  | # list is [ v1, item1, v2, item2, v3, item3, ... , vn, itemn ] | 
| 154 |  |  |  |  |  |  | # match $desired_version between two v's, and return (v,item). | 
| 155 | 7 |  |  |  |  | 23 | for (my $index=0;  $index < scalar(@$list);  $index+=2) { | 
| 156 | 17 |  |  |  |  | 35 | my $ver_at_index= $$list[$index];  # >> might need to normalize it. | 
| 157 | 17 | 100 |  |  |  | 61 | next unless ($ver_at_index ge $desired_version); | 
| 158 |  |  |  |  |  |  | # I get here when I found or passed my spot. | 
| 159 | 5 | 50 |  |  |  | 15 | return @$list[$index, $index+1]  if ($ver_at_index eq $desired_version);  # found it exactly | 
| 160 |  |  |  |  |  |  | # otherwise I passed it. | 
| 161 | 5 | 50 |  |  |  | 12 | return (undef, undef, "desired version not found")  if $index == 0;  # before the first, is not present. | 
| 162 | 5 |  |  |  |  | 31 | return @$list[$index-2, $index-1]; | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  | # after the last, take the last.  Should cap at Module's version, but that was checked earlier when VERSION was called. | 
| 165 | 2 |  |  |  |  | 9 | return @$list[-2,-1]; | 
| 166 |  |  |  |  |  |  | } | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | sub generate_import | 
| 169 |  |  |  |  |  |  | { | 
| 170 | 4 |  |  | 4 | 0 | 7 | my $export_def= shift; | 
| 171 |  |  |  |  |  |  | return sub { | 
| 172 | 13 |  |  | 13 |  | 2103 | my $home= shift; | 
| 173 | 13 |  |  |  |  | 36 | $export_def->setup ($home);  # happens first time used. | 
| 174 | 13 |  |  |  |  | 25 | my $client= _calling_client(); | 
| 175 | 13 |  |  |  |  | 38 | my $version= $export_def->get_import_version ($client); | 
| 176 | 13 |  |  |  |  | 46 | $export_def->callback ('.&begin', $client, $version, '.&begin', \@_); | 
| 177 | 13 | 100 | 100 |  |  | 242 | @_ = ':DEFAULT'  if (!@_ && defined $export_def->{':DEFAULT'}); | 
| 178 | 13 |  |  |  |  | 37 | $export_def -> export ($client, $version, \@_); | 
| 179 | 13 |  |  |  |  | 40 | $export_def->callback ('.&end', $client, $version, '.&begin', \@_); | 
| 180 | 13 |  |  |  |  | 37 | $export_def->_process_worklist(); | 
| 181 | 13 | 100 |  |  |  | 3680 | --$$export_def{'.verbose_import'}  if $$export_def{'.verbose_import'}; | 
| 182 |  |  |  |  |  |  | } | 
| 183 | 4 |  |  |  |  | 29 | } | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | sub export | 
| 186 |  |  |  |  |  |  | { | 
| 187 | 18 |  |  | 18 | 1 | 34 | my ($self, $module, $version, $items)= @_; | 
| 188 | 18 | 50 |  |  |  | 51 | $items= [$items]  unless ref $items eq 'ARRAY';  # allow single item | 
| 189 | 18 |  |  |  |  | 45 | while (my $item= shift @$items) { | 
| 190 | 40 |  |  |  |  | 56 | my $verbose= $$self{'.verbose_import'};  # must check object each time, not cache it. | 
| 191 | 40 | 100 |  |  |  | 80 | print VERBOSE "(Exporter::VA) ===processing import parameter ($item)===\n"    if $verbose; | 
| 192 | 40 | 50 |  |  |  | 69 | if (ref $item) { | 
| 193 | 0 | 0 |  |  |  | 0 | print VERBOSE "(Exporter::VA) It's not a scalar, so invoking .&unknown_type callback.  It's out of my hands.\n"  if $verbose; | 
| 194 | 0 |  |  |  |  | 0 | $self->callback ('.&unknown_type', $module, $version, $item, $items); | 
| 195 |  |  |  |  |  |  | } | 
| 196 |  |  |  |  |  |  | else { | 
| 197 | 40 | 100 | 100 |  |  | 262 | if ($item =~ /^[\$\@\%\*\&]?\w+$/ or $item =~ /^-/ or $item =~s /^(<\w+)>$/$1/) { $self->export_one_symbol ($module,$version,$item,$items) } | 
|  | 35 | 50 | 66 |  |  | 83 |  | 
| 198 | 5 |  |  |  |  | 15 | elsif ($item =~ /^:\w+$/) { $self->export_one_tag ($module,$version,$item, $items) } | 
| 199 |  |  |  |  |  |  | else { | 
| 200 | 0 | 0 |  |  |  | 0 | print VERBOSE "(Exporter::VA) It's not syntactically correct, so invoking .&unknown_feature callback.  It's out of my hands.\n"  if $verbose; | 
| 201 | 0 |  |  |  |  | 0 | $self->callback ('.&unknown_feature', $module, $version, $item, $items); | 
| 202 |  |  |  |  |  |  | } | 
| 203 |  |  |  |  |  |  | } | 
| 204 |  |  |  |  |  |  | } | 
| 205 |  |  |  |  |  |  | } | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | { | 
| 209 |  |  |  |  |  |  | my %thing= reverse (SCALAR=>'$', ARRAY=>'@', HASH=>'%',CODE=>'&',IO=>'<',GLOB=>'*'); | 
| 210 |  |  |  |  |  |  | sub _resolve_by_name | 
| 211 |  |  |  |  |  |  | { | 
| 212 | 12 |  |  | 12 |  | 20 | my ($item, $home, $name)= @_; | 
| 213 | 12 | 100 |  |  |  | 27 | $name= $item  if $name eq '';  # blank string means "same". | 
| 214 | 12 | 100 |  |  |  | 55 | my $sigil= ($name =~ s/^([\$\@\%\&\<\*])//) ? $1  : '&'; | 
| 215 | 12 |  |  |  |  | 18 | my $thing= $thing{$sigil}; | 
| 216 | 12 | 50 |  |  |  | 28 | Err "(Exporter::VA) Improper export definition for item $item: invalid symbol name syntax: $name"  unless defined $thing; | 
| 217 | 1 |  |  | 1 |  | 2000 | no strict 'refs'; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 982 |  | 
| 218 | 12 |  |  |  |  | 12 | my $globref= ${"$home\::"}{$name}; | 
|  | 12 |  |  |  |  | 43 |  | 
| 219 | 12 | 50 |  |  |  | 23 | Err "(Exporter::VA) Symbol to export does not exist: *$home\::$name"  unless defined $globref; | 
| 220 | 12 |  |  |  |  | 13 | return *{$globref}{$thing}; | 
|  | 12 |  |  |  |  | 70 |  | 
| 221 |  |  |  |  |  |  | } # _resolve_by_name | 
| 222 |  |  |  |  |  |  | } # private vars scope | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | sub _resolve_by_versionlist | 
| 225 |  |  |  |  |  |  | { | 
| 226 | 6 |  |  | 6 |  | 32 | my ($self, $module, $desired_version, $item, $param_tail)= @_; | 
| 227 | 6 |  |  |  |  | 13 | my $versionlist= $$self{$item};  # I still have the $item name for callbacks and error messages | 
| 228 | 6 |  |  |  |  | 15 | my ($got_version, $result)= _match_vstring_list ($versionlist, $desired_version); | 
| 229 | 6 | 100 |  |  |  | 33 | print VERBOSE "(Exporter::VA) wanted $item version ", _format_vstring($desired_version), ", choose ", _format_vstring($got_version), "\n" | 
| 230 |  |  |  |  |  |  | if $$self{'.verbose_import'}; | 
| 231 | 6 |  |  |  |  | 22 | return resolve (@_[0..4], $result); | 
| 232 |  |  |  |  |  |  | } | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | sub _resolve_by_hardlink | 
| 235 |  |  |  |  |  |  | { | 
| 236 | 14 |  |  | 14 |  | 26 | my ($item, $hardlink)= @_; | 
| 237 |  |  |  |  |  |  | # This function just provides error checking.  Returning wrong kind of ref can cause problems! | 
| 238 | 14 | 50 |  |  |  | 33 | Err "(Exporter::VA) Improper export definition for item $item: ref to scalar must contain \\\\&code"  unless ref($hardlink) eq 'CODE'; | 
| 239 | 14 |  |  |  |  | 58 | return $hardlink; | 
| 240 |  |  |  |  |  |  | } | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | sub resolve | 
| 243 |  |  |  |  |  |  | { | 
| 244 | 45 |  |  | 45 | 1 | 101 | my ($self, $module, $version, $item, $param_tail, $value)= @_; | 
| 245 | 45 | 100 |  |  |  | 116 | $value= $$self{$item}  unless defined ($value);  # normally lookup, can supply ahead of time for recursive call. | 
| 246 | 45 | 100 | 66 |  |  | 133 | if (!defined $value && $item =~ /^&(.+)$/) { | 
| 247 |  |  |  |  |  |  | # it might be entered in the export def without the sigil. | 
| 248 | 8 |  |  |  |  | 16 | my $base= $1; | 
| 249 | 8 |  |  |  |  | 18 | $value= delete $$self{$base}; | 
| 250 | 8 | 50 |  |  |  | 20 | if (defined $value) { | 
| 251 | 8 |  |  |  |  | 21 | $$self{$item}= $value; | 
| 252 | 8 | 100 |  |  |  | 30 | print VERBOSE "(Exporter::VA) adding leading & to ($base) entry in export definition\n"  if $$self{'.verbose_import'}; | 
| 253 |  |  |  |  |  |  | } | 
| 254 |  |  |  |  |  |  | } | 
| 255 | 45 | 50 |  |  |  | 81 | return $self->callback ('.&unknown_import', $module, $version, $item, $param_tail) | 
| 256 |  |  |  |  |  |  | unless defined $value;  # not listed in export def. | 
| 257 | 45 |  |  |  |  | 66 | my $type= ref $value;  # what is it?  Lots of different ways to list it. | 
| 258 | 45 | 100 |  |  |  | 95 | return _resolve_by_name ($item, $$self{'..home'}, $value)  unless $type;  # scalar is a name in the home package. | 
| 259 | 33 | 100 |  |  |  | 7386 | return &_resolve_by_versionlist  if $type eq 'ARRAY-seen'; | 
| 260 | 28 | 100 |  |  |  | 53 | if ($type eq 'ARRAY') { | 
| 261 | 1 |  |  |  |  | 4 | _normalize_vstring_list ($value); | 
| 262 | 1 |  |  |  |  | 3 | return &_resolve_by_versionlist; | 
| 263 |  |  |  |  |  |  | } | 
| 264 | 27 | 100 |  |  |  | 78 | return $value->(@_)  if $type eq 'CODE'; | 
| 265 | 14 |  |  |  |  | 27 | return _resolve_by_hardlink ($item, $$value); | 
| 266 | 0 |  |  |  |  | 0 | Err "(Exporter::VA) Invalid export definition for item $item"; | 
| 267 |  |  |  |  |  |  | } | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | sub export_one_symbol # or pragma | 
| 270 |  |  |  |  |  |  | { | 
| 271 | 35 |  |  | 35 | 0 | 79 | my ($self, $module, $version, $item, $param_tail)= @_; | 
| 272 | 35 | 100 |  |  |  | 114 | my $sigil= ($item =~ s /^([\$\@\%\*\&\-\<])//) ? $1 : '&'; | 
| 273 | 35 | 50 |  |  |  | 89 | Warn qq((Exporter::VA) warning: importing symbol "$sigil$item" which begins with an underscore) | 
| 274 |  |  |  |  |  |  | if substr($item,0,1) eq '_'; | 
| 275 | 35 |  |  |  |  | 98 | my $X= $self->resolve ($module, $version, "$sigil$item", $param_tail); | 
| 276 | 35 | 100 | 66 |  |  | 486 | if (defined $X && $sigil ne '-') {  # skip the import if it's callback-only | 
| 277 | 31 |  |  |  |  | 63 | my $worklist= $self->worklist(); | 
| 278 | 31 |  |  |  |  | 59 | my $name= "${module}::$item"; | 
| 279 | 31 |  |  |  |  | 73 | $$worklist{$name}= $X;  # duplicates take last resolution with no errors. | 
| 280 | 31 | 100 |  |  |  | 208 | print VERBOSE qq(Got It:  *{"${module}::$item"}= $X\n)  if $$self{'.verbose_import'}; | 
| 281 |  |  |  |  |  |  | } | 
| 282 |  |  |  |  |  |  | } | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  | sub worklist | 
| 285 |  |  |  |  |  |  | { | 
| 286 | 44 |  |  | 44 | 1 | 53 | my $self= shift; | 
| 287 | 44 |  |  |  |  | 82 | return $$self{'..worklist'}; | 
| 288 |  |  |  |  |  |  | } | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  | sub _process_worklist | 
| 291 |  |  |  |  |  |  | { | 
| 292 | 13 |  |  | 13 |  | 17 | my $self= shift; | 
| 293 | 13 |  |  |  |  | 24 | my $worklist= $self->worklist(); | 
| 294 | 1 |  |  | 1 |  | 7 | no strict 'refs'; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 747 |  | 
| 295 | 13 |  |  |  |  | 54 | while (my ($left, $right)= each (%$worklist)) { | 
| 296 | 30 |  |  |  |  | 35 | eval { *{$left}= $right; }; # this better be the right kind of thing! | 
|  | 30 |  |  |  |  | 32 |  | 
|  | 30 |  |  |  |  | 134 |  | 
| 297 | 30 | 50 |  |  |  | 118 | if ($@) { | 
| 298 | 0 |  |  |  |  | 0 | Err "(Exporter::VA) Could not process import for item '$left' = $right."; | 
| 299 |  |  |  |  |  |  | } | 
| 300 |  |  |  |  |  |  | } | 
| 301 |  |  |  |  |  |  | } | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | sub export_one_tag | 
| 304 |  |  |  |  |  |  | { | 
| 305 | 5 |  |  | 5 | 0 | 15 | my ($self, $module, $desired_version, $item, $param_tail)= @_; | 
| 306 | 5 | 50 |  |  |  | 13 | Warn qq((Exporter::VA) warning: importing tag "$item" which begins with an underscore) | 
| 307 |  |  |  |  |  |  | if substr($item,1,1) eq '_'; | 
| 308 |  |  |  |  |  |  | # my $home= $$self{'..home'};  # package I'm exporting =from= | 
| 309 | 6 |  |  |  |  | 11 | RESTART: | 
| 310 |  |  |  |  |  |  | my $list= $$self{$item}; | 
| 311 | 6 | 50 |  |  |  | 14 | Err "(Exporter::VA) no such export tag '$item'"  unless defined $list; | 
| 312 | 6 |  |  |  |  | 9 | my $type= ref $list; | 
| 313 | 6 | 50 |  |  |  | 14 | if ($type eq 'ARRAY') { | 
| 314 |  |  |  |  |  |  | # identify it, and change $type. | 
| 315 | 6 | 50 |  |  |  | 13 | return  if @$list == 0;  # empty list is OK. | 
| 316 | 6 | 100 |  |  |  | 13 | if (is_vstring($$list[0])) { | 
| 317 | 1 |  |  |  |  | 4 | _normalize_vstring_list ($list); | 
| 318 | 1 |  |  |  |  | 2 | $type= 'ARRAY-seen'; | 
| 319 |  |  |  |  |  |  | } | 
| 320 |  |  |  |  |  |  | else { | 
| 321 | 5 |  |  |  |  | 7 | $type= 'ARRAY-tags'; | 
| 322 | 5 |  |  |  |  | 13 | bless $list, $type; | 
| 323 |  |  |  |  |  |  | } | 
| 324 |  |  |  |  |  |  | } | 
| 325 | 6 | 100 |  |  |  | 20 | if ($type eq 'ARRAY-seen') { | 
|  |  | 50 |  |  |  |  |  | 
| 326 | 1 |  |  |  |  | 4 | my ($got_version, $result)= _match_vstring_list ($list, $desired_version); | 
| 327 | 1 | 50 |  |  |  | 8 | print VERBOSE "(Exporter::VA) wanted $item version ", _format_vstring($desired_version), ", choose ", _format_vstring($got_version), "\n" | 
| 328 |  |  |  |  |  |  | if $$self{'.verbose_import'}; | 
| 329 | 1 |  |  |  |  | 2 | $item= $result; | 
| 330 | 1 |  |  |  |  | 9 | goto RESTART; | 
| 331 |  |  |  |  |  |  | } | 
| 332 |  |  |  |  |  |  | elsif ($type eq 'ARRAY-tags') { | 
| 333 | 5 |  |  |  |  | 12 | my @copy= @$list; | 
| 334 | 5 |  |  |  |  | 18 | $self->export ($module, $desired_version, \@copy); | 
| 335 |  |  |  |  |  |  | } | 
| 336 |  |  |  |  |  |  | ## would add support for other types here, e.g. callbacks. | 
| 337 | 0 |  |  |  |  | 0 | else { Err "(Exporter::VA) export tag '$item' is not a list ref" } | 
| 338 |  |  |  |  |  |  | } | 
| 339 |  |  |  |  |  |  |  | 
| 340 |  |  |  |  |  |  | sub callback | 
| 341 |  |  |  |  |  |  | { | 
| 342 | 26 |  |  | 26 | 0 | 32 | my $self= shift; | 
| 343 | 26 |  |  |  |  | 30 | my $cb_name= shift; | 
| 344 | 26 |  |  |  |  | 40 | my $func= $$self{$cb_name}; | 
| 345 |  |  |  |  |  |  | # they should be fully populated, putting in default behavior if it doesn't exist. | 
| 346 |  |  |  |  |  |  | # this is not "try to callback..." so if not found it is an error. | 
| 347 | 26 |  |  |  |  | 61 | $func-> ($self, @_); | 
| 348 |  |  |  |  |  |  | } | 
| 349 |  |  |  |  |  |  |  | 
| 350 |  |  |  |  |  |  | { | 
| 351 |  |  |  |  |  |  | my %defaults= ( | 
| 352 |  |  |  |  |  |  | '.&unknown_type' => sub { Err "(Exporter::VA) import parameter is not a string" }, | 
| 353 |  |  |  |  |  |  | '.&unknown_feature' => sub { Err "(Exporter::VA) import parameter \"$_[3]\" is not syntactically correct" }, | 
| 354 |  |  |  |  |  |  | '.&unknown_import' => sub { Err "(Exporter::VA) import parameter \"$_[3]\" is not listed as an export" }, | 
| 355 |  |  |  |  |  |  | '.check_user_option' => sub { return "unknown option"}, | 
| 356 |  |  |  |  |  |  | '.warnings' => 1, | 
| 357 |  |  |  |  |  |  | '.&begin' => sub {}, | 
| 358 |  |  |  |  |  |  | '.&end' => sub {}, | 
| 359 |  |  |  |  |  |  | '--verbose_import' => sub {  ++$_[0]->{'.verbose_import'} }, | 
| 360 |  |  |  |  |  |  | '--dump' => sub { $_[0]->dump() } | 
| 361 |  |  |  |  |  |  | ); | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | sub _populate_defaults | 
| 365 |  |  |  |  |  |  | { # helper for setup. | 
| 366 |  |  |  |  |  |  | # populate callbacks and settings that were not specified | 
| 367 | 13 |  |  | 13 |  | 15 | my $self= shift; | 
| 368 | 13 |  |  |  |  | 47 | while (my ($key,$value)= each %defaults) { | 
| 369 | 117 | 100 |  |  |  | 406 | $$self{$key}= $value  unless exists $$self{$key}; | 
| 370 |  |  |  |  |  |  | } | 
| 371 | 1 |  |  | 1 |  | 6 | no strict 'refs'; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 393 |  | 
| 372 | 13 | 100 |  |  |  | 35 | $$self{'.default_VERSION'}= normalize_vstring (${"$self->{'..home'}::VERSION"})  unless exists $$self{'.default_VERSION'}; | 
|  | 2 |  |  |  |  | 8 |  | 
| 373 |  |  |  |  |  |  | } | 
| 374 |  |  |  |  |  |  |  | 
| 375 |  |  |  |  |  |  | } # end scope for populate_defaults | 
| 376 |  |  |  |  |  |  |  | 
| 377 |  |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  | sub _expand_plain | 
| 379 |  |  |  |  |  |  | { | 
| 380 | 13 |  |  | 13 |  | 17 | my $self= shift; | 
| 381 | 13 |  |  |  |  | 23 | my $plainspec= delete $$self{'.plain'}; | 
| 382 | 13 | 100 |  |  |  | 35 | return unless defined $plainspec; | 
| 383 | 2 |  |  |  |  | 7 | while (my $value = shift @$plainspec) { | 
| 384 | 11 | 50 |  |  |  | 47 | $value =~ /^([\$\@\&\%:<])?(\w+)>?$/  or Err "(Exporter::VA) item '$value' in .plain list is not a legal symbol or tag name"; | 
| 385 | 11 |  |  |  |  | 24 | my ($sigil, $body)= ($1,$2); | 
| 386 | 11 | 100 |  |  |  | 29 | $sigil= '&'  unless defined $sigil; | 
| 387 | 11 | 100 |  |  |  | 24 | if ($sigil eq ":") { | 
| 388 |  |  |  |  |  |  | # this one is different | 
| 389 | 2 |  |  |  |  | 4 | push @$plainspec, @{$$self{$value}}; | 
|  | 2 |  |  |  |  | 8 |  | 
| 390 |  |  |  |  |  |  | # could do more error checking: make sure tag exists, and doesn't contain v-string list. | 
| 391 |  |  |  |  |  |  | } | 
| 392 | 11 |  |  |  |  | 15 | $value= "$sigil$body"; | 
| 393 | 11 | 100 |  |  |  | 29 | next  if exists $$self{$value}; | 
| 394 | 8 | 100 | 100 |  |  | 44 | next  if $sigil eq '&' && exists $$self{$body};  # present without the leading & for a function | 
| 395 | 5 | 50 | 33 |  |  | 14 | next  if $sigil eq '<' && exists $$self{"$body>"};  # present with trailing > for a handle | 
| 396 |  |  |  |  |  |  | # not already present, so add it. | 
| 397 | 5 |  |  |  |  | 22 | $$self{$value}=$value; | 
| 398 |  |  |  |  |  |  | } | 
| 399 |  |  |  |  |  |  | } | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | sub _get_VERSION | 
| 402 |  |  |  |  |  |  | { | 
| 403 | 15 |  |  | 15 |  | 21 | my $home= shift; | 
| 404 | 1 |  |  | 1 |  | 5 | no strict 'refs'; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 203 |  | 
| 405 | 15 |  |  |  |  | 16 | my $v= ${"${home}::VERSION"}; | 
|  | 15 |  |  |  |  | 50 |  | 
| 406 | 15 | 50 |  |  |  | 35 | Err "(Exporter::VA) module $home does not contain a package global \$VERSION" | 
| 407 |  |  |  |  |  |  | unless defined $v; | 
| 408 | 15 |  |  |  |  | 32 | return normalize_vstring ($v); | 
| 409 |  |  |  |  |  |  | } | 
| 410 |  |  |  |  |  |  |  | 
| 411 |  |  |  |  |  |  | { # extra scope for variable local to function | 
| 412 |  |  |  |  |  |  |  | 
| 413 |  |  |  |  |  |  | my %check_code= ( | 
| 414 |  |  |  |  |  |  | # could point to more detailed checking function, or just 1 for OK/allowed with no additional testing. | 
| 415 |  |  |  |  |  |  | '.allowed_VERSIONS' => 1, | 
| 416 |  |  |  |  |  |  | '.&begin' => 1, | 
| 417 |  |  |  |  |  |  | '.check_user_option' => 1, | 
| 418 |  |  |  |  |  |  | '.default_VERSION' => 1, | 
| 419 |  |  |  |  |  |  | '.&end' => 1, | 
| 420 |  |  |  |  |  |  | '.plain' => 1, | 
| 421 |  |  |  |  |  |  | '.&unknown_feature' => 1, | 
| 422 |  |  |  |  |  |  | '.&unknown_import' => 1, | 
| 423 |  |  |  |  |  |  | '.&unknown_type' => 1, | 
| 424 |  |  |  |  |  |  | '.verbose_import' => 1, | 
| 425 |  |  |  |  |  |  | '.warnings' => 1 | 
| 426 |  |  |  |  |  |  | ); | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | sub _check_warning_option($$$) | 
| 429 |  |  |  |  |  |  | { | 
| 430 | 161 |  |  | 161 |  | 239 | my ($self, $item, $value)= @_; | 
| 431 | 1 | 50 |  | 1 |  | 6 | if ($item =~ /^\.&?\p{IsUpper}/) { | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 12 |  | 
|  | 161 |  |  |  |  | 531 |  | 
| 432 |  |  |  |  |  |  | # a user-defined option. | 
| 433 | 0 |  |  |  |  | 0 | $self->check_user_option ($item); | 
| 434 |  |  |  |  |  |  | } | 
| 435 | 161 | 100 |  |  |  | 483 | return if $item =~ /^\.\./;  # internal state information | 
| 436 |  |  |  |  |  |  | # check for known options. | 
| 437 | 111 |  |  |  |  | 136 | my $checker= $check_code{$item}; | 
| 438 | 111 | 50 |  |  |  | 580 | if (!defined $checker)  { Warn qq{(Exporter::VA) unknown option present: "$item"} } | 
|  | 0 | 50 |  |  |  | 0 |  | 
| 439 | 0 |  |  |  |  | 0 | elsif (ref $checker) { $checker->($item,$value) } | 
| 440 |  |  |  |  |  |  | # else it exists but doesn't have special checking code, so no messages. | 
| 441 |  |  |  |  |  |  | } | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | } # scope for _check_warning_option | 
| 444 |  |  |  |  |  |  |  | 
| 445 |  |  |  |  |  |  | sub _check_warning_tag($$) | 
| 446 |  |  |  |  |  |  | { | 
| 447 | 30 |  |  | 30 |  | 106 | my ($item, $value)= @_; | 
| 448 |  |  |  |  |  |  | } | 
| 449 |  |  |  |  |  |  |  | 
| 450 |  |  |  |  |  |  | sub _check_warning_pragma($$) | 
| 451 |  |  |  |  |  |  | { | 
| 452 | 29 |  |  | 29 |  | 107 | my ($item, $value)= @_; | 
| 453 |  |  |  |  |  |  | } | 
| 454 |  |  |  |  |  |  |  | 
| 455 |  |  |  |  |  |  | sub _check_warning_identifier($$) | 
| 456 |  |  |  |  |  |  | { | 
| 457 | 67 |  |  | 67 |  | 252 | my ($item, $value)= @_; | 
| 458 |  |  |  |  |  |  | } | 
| 459 |  |  |  |  |  |  |  | 
| 460 |  |  |  |  |  |  | sub _check_for_warnings | 
| 461 |  |  |  |  |  |  | { | 
| 462 | 13 |  |  | 13 |  | 17 | my $self= shift; | 
| 463 | 13 |  |  |  |  | 47 | while (my($key, $value)= each %$self) { | 
| 464 | 287 |  |  |  |  | 362 | my $firstchar= substr($key,0,1); | 
| 465 | 287 | 100 |  |  |  | 569 | if ($firstchar eq '.')  { _check_warning_option ($self, $key, $value) } | 
|  | 161 | 100 |  |  |  | 249 |  | 
|  | 30 | 100 |  |  |  | 49 |  | 
| 466 | 29 |  |  |  |  | 48 | elsif ($firstchar eq ':')  {_check_warning_tag ($key, $value) } | 
| 467 | 67 |  |  |  |  | 101 | elsif ($firstchar eq '-')	{_check_warning_pragma ($key, $value) } | 
| 468 |  |  |  |  |  |  | else  {_check_warning_identifier ($key, $value) } | 
| 469 |  |  |  |  |  |  | } | 
| 470 |  |  |  |  |  |  | } | 
| 471 |  |  |  |  |  |  |  | 
| 472 |  |  |  |  |  |  | sub setup | 
| 473 |  |  |  |  |  |  | { | 
| 474 | 13 |  |  | 13 | 0 | 19 | my ($self, $home)= @_; | 
| 475 | 13 |  |  |  |  | 28 | my $existing_home= $$self{'..home'}; | 
| 476 | 13 | 50 | 66 |  |  | 62 | if (defined $existing_home && $existing_home ne $home) { | 
| 477 | 0 |  |  |  |  | 0 | Err "(Exporter::VA) reuse of \%EXPORT in module $home is not allowed."; | 
| 478 |  |  |  |  |  |  | } | 
| 479 | 13 |  |  |  |  | 25 | $$self{'..home'}= $home; | 
| 480 | 13 |  |  |  |  | 28 | $$self{'..worklist'}= {}; | 
| 481 | 13 |  |  |  |  | 43 | $$self{'..max_VERSION'}= _get_VERSION ($home); | 
| 482 | 13 |  |  |  |  | 37 | $self->_expand_plain(); | 
| 483 | 13 |  |  |  |  | 26 | $self->_populate_defaults(); | 
| 484 | 13 | 100 |  |  |  | 35 | if (exists $$self{'.allowed_VERSIONS'}) { | 
| 485 | 3 |  |  |  |  | 4 | $_= normalize_vstring($_)  foreach (@{$$self{'.allowed_VERSIONS'}}); | 
|  | 3 |  |  |  |  | 11 |  | 
| 486 |  |  |  |  |  |  | } | 
| 487 | 13 | 50 |  |  |  | 50 | $self->_check_for_warnings()  if $$self{'.warnings'}; | 
| 488 |  |  |  |  |  |  | } | 
| 489 |  |  |  |  |  |  |  | 
| 490 |  |  |  |  |  |  | { | 
| 491 |  |  |  |  |  |  | my $client_export_def; | 
| 492 |  |  |  |  |  |  |  | 
| 493 |  |  |  |  |  |  | sub begin | 
| 494 |  |  |  |  |  |  | { | 
| 495 | 4 |  |  | 4 | 0 | 11 | my ($blessed_export_def, $caller, $version, $symbol, $param_list_tail)= @_; | 
| 496 | 4 |  |  |  |  | 10 | $client_export_def= find_export_def ($caller, $param_list_tail); | 
| 497 |  |  |  |  |  |  | } | 
| 498 |  |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | sub find_export_def | 
| 500 |  |  |  |  |  |  | { | 
| 501 | 4 |  |  | 4 | 1 | 7 | my ($caller, $params)= @_; | 
| 502 |  |  |  |  |  |  | # first, try to locate hash ref in parameter list. | 
| 503 | 4 |  |  |  |  | 13 | foreach my $index (0..scalar(@$params)-1) { | 
| 504 | 9 |  |  |  |  | 16 | my $val= $$params[$index]; | 
| 505 | 9 | 100 |  |  |  | 25 | if (ref ($val) eq 'HASH') { | 
| 506 | 2 |  |  |  |  | 7 | splice @$params, $index, 1;  # remove it | 
| 507 | 2 | 100 | 66 |  |  | 18 | splice @$params, $index-1, 1 | 
| 508 |  |  |  |  |  |  | if $index>0 && $$params[$index-1] eq '-def';  # remove optional explicit switch | 
| 509 | 2 |  |  |  |  | 9 | return $val;  # return it | 
| 510 |  |  |  |  |  |  | } | 
| 511 |  |  |  |  |  |  | } | 
| 512 |  |  |  |  |  |  | # look for package variable in caller. | 
| 513 | 1 |  |  | 1 |  | 27687 | no strict 'refs'; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 341 |  | 
| 514 | 2 |  |  |  |  | 3 | return \%{"$caller\::EXPORT"}; | 
|  | 2 |  |  |  |  | 16 |  | 
| 515 |  |  |  |  |  |  | } | 
| 516 |  |  |  |  |  |  |  | 
| 517 |  |  |  |  |  |  | sub export_import | 
| 518 |  |  |  |  |  |  | # called to export a custom import function to *my* client, when Export::VA is used. | 
| 519 |  |  |  |  |  |  | { | 
| 520 | 3 |  |  | 3 | 0 | 7 | my ($VA_export_def, $caller, $version, $symbol, $param_list_tail)= @_; | 
| 521 | 3 |  |  |  |  | 8 | $client_export_def= bless $client_export_def, "Exporter::VA"; | 
| 522 | 3 |  |  |  |  | 8 | return generate_import ($client_export_def); | 
| 523 |  |  |  |  |  |  | } | 
| 524 |  |  |  |  |  |  |  | 
| 525 |  |  |  |  |  |  |  | 
| 526 |  |  |  |  |  |  | sub export_VERSION | 
| 527 |  |  |  |  |  |  | # called to export a custom VERSION function to *my* client, when Export::VA is used. | 
| 528 |  |  |  |  |  |  | { | 
| 529 |  |  |  |  |  |  | # my ($VA_export_def, $caller, $version, $symbol, $param_list_tail)= @_; | 
| 530 |  |  |  |  |  |  | # the above line documents the parameters, but I don't need any of them so it's commented out. | 
| 531 | 3 |  |  | 3 | 0 | 9 | return generate_VERSION ($client_export_def); | 
| 532 |  |  |  |  |  |  | } | 
| 533 |  |  |  |  |  |  |  | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | sub export_AUTOLOAD | 
| 536 |  |  |  |  |  |  | # called to export a custom AUTOLOAD function to *my* client, when Export::VA is used. | 
| 537 |  |  |  |  |  |  | { | 
| 538 |  |  |  |  |  |  | # my ($VA_export_def, $caller, $version, $symbol, $param_list_tail)= @_; | 
| 539 |  |  |  |  |  |  | # the above line documents the parameters, but I don't need any of them so it's commented out. | 
| 540 | 1 |  |  | 1 | 0 | 5 | return _generate_AUTOLOAD ($client_export_def); | 
| 541 |  |  |  |  |  |  | } | 
| 542 |  |  |  |  |  |  |  | 
| 543 |  |  |  |  |  |  |  | 
| 544 |  |  |  |  |  |  | }  # end scope around $client_export_def | 
| 545 |  |  |  |  |  |  |  | 
| 546 |  |  |  |  |  |  | sub autoload_symbol | 
| 547 |  |  |  |  |  |  | { | 
| 548 | 3 |  |  | 3 | 1 | 47 | my ($self, $symbol, @extra)= @_; | 
| 549 | 3 |  |  |  |  | 4 | my %memory; | 
| 550 | 3 |  |  |  |  | 7 | my $home= $self->{'..home'}; | 
| 551 |  |  |  |  |  |  | my $thunk= sub { | 
| 552 | 4 |  |  | 4 |  | 209 | my $retval= eval { | 
| 553 | 4 |  |  |  |  | 10 | my $caller= _calling_client();  # so I don't have to figure it out multiple times | 
| 554 | 4 |  |  |  |  | 7 | my $f= $memory{$caller}; | 
| 555 | 4 | 50 |  |  |  | 14 | unless (defined $f) { | 
| 556 | 4 |  |  |  |  | 16 | $f= $memory{$caller}= $self->resolve ($caller, $home->VERSION(undef,$caller), '&'.$symbol, [@extra]); | 
| 557 |  |  |  |  |  |  | } | 
| 558 | 4 |  |  |  |  | 42 | goto &$f; | 
| 559 |  |  |  |  |  |  | }; | 
| 560 | 4 | 50 |  |  |  | 14 | if ($@) { | 
| 561 | 4 |  |  |  |  | 572 | carp "(Exporter::VA) Cannot redirect to versioned function ($@)"; | 
| 562 |  |  |  |  |  |  | } | 
| 563 | 4 |  |  |  |  | 36 | return $retval; | 
| 564 | 3 |  |  |  |  | 20 | }; | 
| 565 | 1 |  |  | 1 |  | 6 | no strict 'refs'; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 202 |  | 
| 566 | 3 |  |  |  |  | 4 | *{"${home}::$symbol"}= $thunk; | 
|  | 3 |  |  |  |  | 17 |  | 
| 567 |  |  |  |  |  |  | } | 
| 568 |  |  |  |  |  |  |  | 
| 569 |  |  |  |  |  |  | sub _generate_AUTOLOAD | 
| 570 |  |  |  |  |  |  | { | 
| 571 | 1 |  |  | 1 |  | 39 | my $client_export_def= shift; | 
| 572 |  |  |  |  |  |  | return sub {  # the generated AUTOLOAD | 
| 573 | 3 |  |  | 3 |  | 184 | my $AUTOLOAD= $Exporter::VA::AUTOLOAD;  # save the global in case of recursion. | 
| 574 | 3 |  |  |  |  | 5 | my $func= $AUTOLOAD; | 
| 575 | 3 |  |  |  |  | 13 | $func =~ s/.*:://;  # not checking the actual module name.  Might be inherited or re-routed or something.  I shouldn't care, right? | 
| 576 | 3 | 100 | 66 |  |  | 29 | Err "(Exporter::VA) Generated $client_export_def->{'..home'}::AUTOLOAD can't find export definition for $func." | 
| 577 |  |  |  |  |  |  | unless exists $client_export_def->{$func} || exists $client_export_def->{'&' . $func}; | 
| 578 | 2 |  |  |  |  | 6 | $client_export_def->autoload_symbol ($func); | 
| 579 | 2 |  |  |  |  | 7 | goto &$AUTOLOAD;  # try it again. | 
| 580 |  |  |  |  |  |  | } | 
| 581 | 1 |  |  |  |  | 8 | } | 
| 582 |  |  |  |  |  |  |  | 
| 583 |  |  |  |  |  |  | ## main code. | 
| 584 |  |  |  |  |  |  | { | 
| 585 |  |  |  |  |  |  |  | 
| 586 |  |  |  |  |  |  | my $export_def= bless (\%EXPORT, __PACKAGE__); | 
| 587 | 1 |  |  | 1 |  | 6 | use vars qw/*import *VERSION/;  # silence a warning when syntax checking the .pm by itself | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 122 |  | 
| 588 |  |  |  |  |  |  | *import= generate_import ($export_def); | 
| 589 |  |  |  |  |  |  | *VERSION= generate_VERSION ($export_def); | 
| 590 |  |  |  |  |  |  | } | 
| 591 |  |  |  |  |  |  |  | 
| 592 |  |  |  |  |  |  |  | 
| 593 |  |  |  |  |  |  | 1; | 
| 594 |  |  |  |  |  |  | __END__ |