File Coverage

blib/lib/Function/Override.pm
Criterion Covered Total %
statement 89 92 96.7
branch 24 42 57.1
condition n/a
subroutine 11 11 100.0
pod 0 4 0.0
total 124 149 83.2


line stmt bran cond sub pod time code
1             package Function::Override;
2              
3 2     2   48027 use Carp;
  2         5  
  2         188  
4 2     2   11 use strict;
  2         3  
  2         75  
5 2     2   11 use vars qw( $Debug $VERSION @EXPORT );
  2         8  
  2         133  
6              
7 2     2   10 use base qw(Exporter);
  2         8  
  2         2495  
8              
9             $VERSION = '0.03';
10             @EXPORT = qw(override);
11              
12             $Debug = $ENV{PERL_FUNCTION_OVERRIDE_DEBUG} || 0 unless defined $Debug;
13              
14             sub override {
15 3     3 0 4006 my($sym, $callback, $pkg) = @_;
16 3 50       17 $pkg = caller() unless defined $pkg;
17 3         13 &_override_function($sym, $callback, $pkg);
18             };
19              
20             sub fill_protos {
21 3     3 0 7 my $proto = shift;
22 3         8 my ($n, $isref, @out, @out1, $seen_semi) = -1;
23 3         15 while ($proto =~ /\S/) {
24 6         13 $n++;
25 6 100       19 push(@out1,[$n,@out]) if $seen_semi;
26 6 50       18 push(@out, $1 . "{\$_[$n]}"), next if $proto =~ s/^\s*\\([\@%\$\&])//;
27 6 100       74 push(@out, "\$_[$n]"), next if $proto =~ s/^\s*([*\$&_])//;
28 3 100       23 push(@out, "\@_[$n..\$#_]"), last if $proto =~ s/^\s*(;\s*)?\@//;
29 1 50       7 $seen_semi = 1, $n--, next if $proto =~ s/^\s*;//; # XXXX ????
30 0         0 die "Unknown prototype letters: \"$proto\"";
31             }
32 3         16 push(@out1,[$n+1,@out]);
33 3         11 @out1;
34             }
35              
36             sub write_invocation {
37 3     3 0 8 my ($core, $call, $name, @argvs) = @_;
38 3 100       8 if (@argvs == 1) { # No optional arguments
39 2         2 my @argv = @{$argvs[0]};
  2         6  
40 2         3 shift @argv;
41 2         6 return "\t" . one_invocation($core, $call, $name, @argv) . ";\n";
42             } else {
43 1         2 my $else = "\t";
44 1         1 my (@out, @argv, $n);
45 1         5 while (@argvs) {
46 3         5 @argv = @{shift @argvs};
  3         9  
47 3         4 $n = shift @argv;
48 3         9 push @out, "$ {else}if (\@_ == $n) {\n";
49 3         4 $else = "\t} els";
50 3         7 push @out,
51             "\t\treturn " . one_invocation($core, $call, $name, @argv) . ";\n";
52             }
53 1         4 push @out, <
54             }
55             die "$name(\@_): Do not expect to get ", scalar \@_, " arguments";
56             EOC
57 1         6 return join '', @out;
58             }
59             }
60              
61             sub one_invocation {
62 5     5 0 12 my ($core, $call, $name, @argv) = @_;
63 5         7 local $" = ', ';
64 5         32 return qq{$call(@argv)};
65             }
66              
67             sub _override_function {
68 3     3   7 my($sub, $callback, $pkg) = @_;
69 3         7 my($name, $code, $sref, $real_proto, $proto, $core, $call);
70 3         5 my $ini = $sub;
71              
72 3 50       14 $sub = "${pkg}::$sub" unless $sub =~ /::/;
73 3         5 $name = $sub;
74 3 50       33 $name =~ s/.*::// or $name =~ s/^&//;
75 3 50       11 print "# _override_function: sub=$sub pkg=$pkg name=$name\n" if $Debug;
76 3 50       17 croak "Bad subroutine name for Function::Override: $name"
77             unless $name =~ /^\w+$/;
78 3 100       19 if (defined(&$sub)) { # user subroutine
    50          
79 2         5 $sref = \&$sub;
80 2         4 $proto = prototype $sref;
81 2         4 $call = '&$sref';
82             } elsif ($sub eq $ini) { # Stray user subroutine
83 0         0 die "$sub is not a Perl subroutine"
84             } else { # CORE subroutine
85 1         2 $proto = eval { prototype "CORE::$name" };
  1         17  
86 1 50       5 die "$name is neither a builtin, nor a Perl subroutine"
87             if $@;
88 1 50       4 die "Cannot override the non-overridable builtin '$name'"
89             if not defined $proto;
90 1         2 $core = 1;
91 1         2 $call = "CORE::$name";
92             }
93 3 100       14 if (defined $proto) {
94 2         5 $real_proto = " ($proto)";
95             } else {
96 1         2 $real_proto = '';
97 1         2 $proto = '@';
98             }
99 3         19 $code = <
100             sub$real_proto {
101             local(\$", \$!) = (', ', 0);
102             \$callback->(\@_);
103             EOS
104 3         9 my @protos = fill_protos($proto);
105 3         11 $code .= write_invocation($core, $call, $name, @protos);
106 3         8 $code .= "}\n";
107             {
108 2     2   14 no strict 'refs'; # to avoid: Can't use string (...) as a symbol ref ...
  2         3  
  2         264  
  3         4  
109 3         8 $code = <<"CODE";
110             package $pkg;
111             $code
112             CODE
113              
114 3 50       8 print $code if $Debug;
115              
116 3 0   3   560 $code = eval($code);
  3 0       2126  
  3 0       13  
  2         93  
  1         35  
  1         4  
  1         34  
  0            
117 3 50       12 die if $@;
118 3         13 local($^W) = 0; # to avoid: Subroutine foo redefined ...
119 3         6 *{$sub} = $code;
  3         3378  
120             }
121             }
122              
123             1;
124              
125             __END__