File Coverage

lib/Perlmazing/Engine.pm
Criterion Covered Total %
statement 121 164 73.7
branch 25 54 46.3
condition 18 35 51.4
subroutine 19 24 79.1
pod 4 4 100.0
total 187 281 66.5


line stmt bran cond sub pod time code
1             package Perlmazing::Engine;
2 34     34   13484 use Perlmazing::Feature;
  34         85  
  34         247  
3 34     34   17745 use Submodules;
  34         190006  
  34         207  
4 34     34   1384 use Carp;
  34         57  
  34         2363  
5 34     34   195 use Scalar::Util qw(set_prototype);
  34         64  
  34         1731  
6 34     34   18920 use Taint::Util 'untaint';
  34         20010  
  34         190  
7 34     34   20320 use Data::Dump 'dump';
  34         214042  
  34         34114  
8             our $VERSION = '1.2812';
9             my $found_symbols;
10             my $loaded_symbols;
11             my $precompile_symbols;
12             my $parameters;
13              
14             sub found_symbols {
15 34     34 1 105 my $self = shift;
16 34   33     294 my $package = (shift or caller);
17 34 50       162 return unless exists $found_symbols->{$package};
18 34         68 sort keys %{$found_symbols->{$package}};
  34         3407  
19             }
20              
21             sub loaded_symbols {
22 0     0 1 0 my $self = shift;
23 0   0     0 my $package = (shift or caller);
24 0 0       0 return unless exists $loaded_symbols->{$package};
25 0         0 return sort keys %{$loaded_symbols->{$package}};
  0         0  
26             }
27              
28             sub preload {
29 34     34 1 80 my $self = shift;
30 34         122 my $package = caller;
31 34         130 for my $i (@_) {
32 0         0 _debug("Preloading symbol $i", $package);
33 0         0 _load_symbol($package, $i);
34             }
35             }
36              
37             sub import {
38 34     34   1639 my $self = shift;
39 34 50       4170 carp "If passing arguments to this module, it most be using named arguments" if @_ % 2;
40 34         92 my $package = caller;
41 34         3193 my $p = {@_};
42 34 50       8401 $parameters->{$package} = {} unless $parameters->{$package};
43 34         1645 $parameters->{$package} = {%{$parameters->{$package}}, %$p};
  34         117  
44 34 50       3987 return if exists $found_symbols->{$package};
45 34         5447 $found_symbols->{$package} = {};
46 34         4074 _debug("Importing $self");
47 34         112 $self->_find_symbols($package);
48             }
49              
50             sub _is_compile_phase {
51 0     0   0 my $code = q[
52             BEGIN {
53             use warnings 'FATAL' => 'all';
54             eval 'INIT{} 1' or die;
55             }
56             ];
57 0         0 eval $code;
58 0 0       0 return 0 if $@;
59 0         0 1;
60             }
61              
62             sub _debug {
63 13790     13790   19297 my $msg = shift;
64 13790   66     26920 my $caller = (shift or caller);
65 13790 50       34056 print STDERR __PACKAGE__." DEBUG: Package $caller, $msg\n" if $parameters->{$caller}->{debug};
66             }
67              
68             sub _find_symbols {
69 34     34   58 my $self = shift;
70 34         47 my $package = shift;
71 34         60 my @paths;
72             my $seen_paths;
73 34         125 _debug("Looking for symbols", $package);
74 34         287 for my $i (Submodules->find("${package}::Perlmazing")) {
75 12240 100       2414384 next if $i->{Clobber};
76 4080         11326 _debug("Found file $i->{AbsPath} for symbol $i->{Name}", $package);
77 4080         34214 $found_symbols->{$package}->{$i->Name} = {%$i};
78 4080 100       55095 if ($i->Module eq "${package}::Perlmazing::Precompile::$i->{Name}") {
79 1904         23157 $precompile_symbols->{$package}->{$i->Name} = $i;
80             }
81 34     34   395 no strict 'refs';
  34         122  
  34         1545  
82 34     34   159 no warnings; # prevents during-cleanup warnings when Submodules was destroyed and $i is undefined
  34         83  
  34         56685  
83 4080         47638 my $name = $i->Name;
84 4080         19378 *{"${package}::$i->{Name}"} = sub {
85 34     34   2290972 unshift @_, $package, $name;
86 34         173 goto &_autoload;
87 4080         48920 };
88             }
89             }
90              
91             sub precompile {
92 34     34 1 98 my $self = shift;
93 34         85 my $package = caller;
94 34 50       163 return unless exists $precompile_symbols->{$package};
95 34         72 for my $name (sort keys %{$precompile_symbols->{$package}}) {
  34         950  
96             # We detect already precompiled symbols by undefining this variable
97             # Note that symbols can in some cases (by internal recursion) be called
98             # more than once, not allowing _load_symbol to complete and mark it as loaded
99             # before being called again, so this part comes handy in those cases.
100 1904 50       5540 next if not defined $precompile_symbols->{$package}->{$name};
101 1904         9157 undef $precompile_symbols->{$package}->{$name};
102 1904         52099 _debug("Precompiling symbol $name", $package);
103 1904         3987 _load_symbol($package, $name);
104             }
105             }
106              
107             sub _preload {
108 0     0   0 my $self = shift;
109 0         0 my $package = shift;
110 0         0 for my $i (@_) {
111 0         0 _debug("Preloading symbol $i", $package);
112 0         0 _load_symbol($package, $i);
113             }
114             }
115              
116             sub _autoload {
117 34     34   108 my ($package, $symbol) = (shift, shift);
118 34         179 _debug("Autoloading symbol $symbol", $package);
119 34         113 my $code = _load_symbol($package, $symbol);
120 34         149 goto $code;
121             }
122              
123             sub _load_symbol {
124 1938     1938   3794 my ($package, $symbol) = (shift, shift);
125 1938         2592 local $@;
126 1938 100 100     7937 return $loaded_symbols->{$package}->{$symbol} if exists $loaded_symbols->{$package} and exists $loaded_symbols->{$package}->{$symbol};
127 1926 50 33     7471 croak "File $package/Perlmazing/$symbol.pm cannot be found in \@INC for symbol \&${package}::$symbol - \@INC contains: @INC" unless exists $found_symbols->{$package} and exists $found_symbols->{$package}->{$symbol};
128 1926         6301 _debug("Reading file $found_symbols->{$package}->{$symbol}->{AbsPath}", $package);
129 1926         5753 my $code = Submodules::Result::read($found_symbols->{$package}->{$symbol});
130 1926         263080 _debug("Parsing contents of $found_symbols->{$package}->{$symbol}->{AbsPath}", $package);
131 1926         3088 my $stderr = '';
132 1926         4727 my $eval_string = "\n#line 1 $found_symbols->{$package}->{$symbol}->{AbsPath}\npackage ${package}::Perlmazing::$symbol; $code";
133             {
134 1926         2701 local *STDERR;
  1926         4903  
135 1926         12633 open STDERR, '>>', \$stderr;
136 1926         5739 untaint $eval_string;
137 1926         827775 eval $eval_string;
138             }
139 1926 50       84959 if (my $e = $@) {
140 0         0 croak "While attempting to load symbol '$symbol': $e";
141             }
142 1926 50       4490 print STDERR $stderr if length $stderr;
143 1926         28889 $loaded_symbols->{$package}->{$symbol} = "${package}::Perlmazing::${symbol}"->can('main');
144 1926 50       6201 die "Unable to find sub 'main' at $found_symbols->{$package}->{$symbol}->{AbsPath} line 1 to EOF\n" unless $loaded_symbols->{$package}->{$symbol};
145 1926         8570 _debug("Replacing skeleton symbol with actual code from $found_symbols->{$package}->{$symbol}->{AbsPath}", $package);
146 1926 50       16284 if ("${package}::Perlmazing::${symbol}"->isa('Perlmazing::Listable')) {
147 0         0 _debug("Symbol &${package}::$symbol isa Perlmazing::Listable, creating wrapper sub around it", $package);
148 0         0 my $sub_main = $loaded_symbols->{$package}->{$symbol};
149             my $sub_pre = sub {
150 0     0   0 for my $i (@_) {
151 0         0 $sub_main->($i);
152             }
153 0         0 };
154             $loaded_symbols->{$package}->{$symbol} = sub {
155 0     0   0 my $wantarray = wantarray;
156 0         0 my @call = caller(1);
157 0         0 my @res = eval {
158 0 0       0 (@_) = ($_) if not @_;
159 0 0       0 if ($wantarray) {
    0          
160 0         0 my @res = @_;
161 0         0 foreach my $i (@res) {
162 0         0 $sub_pre->($i);
163             }
164 0         0 return @res;
165             } elsif (defined $wantarray) {
166 0         0 my $i = $_[0];
167 0         0 $sub_pre->($i);
168 0         0 return $i;
169             } else {
170 0         0 foreach my $i (@_) {
171 0         0 $sub_pre->($i);
172             }
173             }
174             };
175 0 0       0 if (my $e = $@) {
176 0 0       0 if ($e =~ /^Modification of a read\-only value attempted/) {
177 0         0 die "Modification of a read-only value attempted at $call[1] line $call[2]\n";
178             } else {
179 0         0 die "Unhandled error in listable function ${package}::$symbol: $e\n";
180             }
181             }
182 0 0       0 return @res if $wantarray;
183 0         0 $res[0];
184 0         0 };
185             }
186 34     34   322 no strict 'refs';
  34         60  
  34         2443  
187 34     34   220 no warnings qw(redefine once);
  34         2371  
  34         25913  
188 1926         4418 my $skeleton = *{"${package}::$symbol"}{CODE};
  1926         6051  
189 1926         3227 my ($callers, $offset);
190 1926         6972 while (my $caller = caller($offset++)) {
191 9847         23582 $callers->{$caller}++;
192             }
193 1926         2880 $callers->{$package}++;
194 1926         6668 for my $i (keys %$callers) {
195 5836 100       11264 next if $i eq __PACKAGE__;
196 3910 100 100     4292 if (exists ${"${i}::"}{$symbol} and my $ref = *{"${i}::$symbol"}{CODE}) {
  3910         13512  
  1948         8069  
197 1948         2362 my $proto_old = prototype \&{"${i}::$symbol"};
  1948         4827  
198 1948         5007 my $proto_new = prototype $loaded_symbols->{$package}->{$symbol};
199 1948 100 66     17286 if ((defined $proto_new and defined $proto_old and $proto_old ne $proto_new) or (defined $proto_old and not defined $proto_new) or (defined $proto_new and not defined $proto_old)) {
      33        
      33        
      33        
      66        
      66        
200             # Disabling this as it is noisy and seems to be somewhat unimportant for general use. Leaving it commented in case I change my mind in a future version.
201             #carp "Warning: Too late to apply prototype ($proto_new) to symbol &${i}::$symbol - perl compilation phase has passed already" unless _is_compile_phase();
202 1700         2280 set_prototype \&{"${i}::$symbol"}, $proto_new;
  1700         11171  
203             }
204 1948 50       6775 *{"${i}::$symbol"} = $loaded_symbols->{$package}->{$symbol} if $ref eq $skeleton;
  1948         9283  
205             }
206             }
207 1926         7263 _debug(__PACKAGE__." no longer has power over symbol &${package}::$symbol (it's now loaded on its own code)", $package);
208 1926         13283 $loaded_symbols->{$package}->{$symbol};
209             }
210              
211             package Perlmazing::Listable;
212              
213             1;
214              
215             __END__