File Coverage

blib/lib/Aspect/Advice/Before.pm
Criterion Covered Total %
statement 94 103 91.2
branch 38 46 82.6
condition n/a
subroutine 12 12 100.0
pod n/a
total 144 161 89.4


line stmt bran cond sub pod time code
1             package Aspect::Advice::Before;
2              
3 46     46   5296 use strict;
  41         77  
  41         1108  
4              
5             # Added by eilara as hack around caller() core dump
6             # NOTE: Now we've switched to Sub::Uplevel can this be removed? --ADAMK
7 41     26   165 use Carp::Heavy ();
  41         184  
  38         409  
8 38     26   459 use Carp ();
  27         55  
  26         337  
9 27     26   145 use Aspect::Hook ();
  37         75  
  37         467  
10 31     26   4068 use Aspect::Advice ();
  31         66  
  31         609  
11 31     26   131 use Aspect::Point ();
  31         127  
  29         4236  
12              
13             our $VERSION = '1.04';
14             our @ISA = 'Aspect::Advice';
15              
16             sub _install {
17 50     46   1545 my $self = shift;
18 50         133 my $pointcut = $self->pointcut;
19 50         261 my $code = $self->code;
20 50         207 my $lexical = $self->lexical;
21              
22             # Get the curried version of the pointcut we will use for the
23             # runtime checks instead of the original.
24             # Because $MATCH_RUN is used in boolean conditionals, if there
25             # is nothing to do the compiler will optimise away the code entirely.
26 50         273 my $curried = $pointcut->curry_runtime;
27 49 100       264 my $compiled = $curried ? $curried->compiled_runtime : undef;
28 49 100       188 my $MATCH_RUN = $compiled ? '$compiled->()' : 1;
29              
30             # When an aspect falls out of scope, we don't attempt to remove
31             # the generated hook code, because it might (for reasons potentially
32             # outside our control) have been recursively hooked several times
33             # by both Aspect and other modules.
34             # Instead, we store an "out of scope" flag that is used to shortcut
35             # past the hook as quickely as possible.
36             # This flag is shared between all the generated hooks for each
37             # installed Aspect.
38             # If the advice is going to last lexical then we don't need to
39             # check or use the $out_of_scope variable.
40 47         83 my $out_of_scope = undef;
41 46 100       124 my $MATCH_DISABLED = $lexical ? '$out_of_scope' : '0';
42              
43             # Find all pointcuts that are statically matched
44             # wrap the method with advice code and install the wrapper
45 47         336 foreach my $name ( $pointcut->match_all ) {
46 49         125 my $NAME = $name; # For completeness
47              
48 29     26   293 no strict 'refs';
  27         62  
  26         2827  
49 49         259 my $original = *$name{CODE};
50 47 100       167 unless ( $original ) {
51 0         0 Carp::croak("Can't wrap non-existent subroutine ", $name);
52             }
53              
54             # Any way to set prototypes other than eval?
55 47         119 my $PROTOTYPE = prototype($original);
56 47 100       252 $PROTOTYPE = defined($PROTOTYPE) ? "($PROTOTYPE)" : '';
57              
58             # Generate the new function
59 27     26   157 no warnings 'redefine';
  28         61  
  28         6873  
60 47 50   32   14816 eval <<"END_PERL"; die $@ if $@;
  47 100       207  
  32 100       3903  
  24 100       51  
  24 100       204  
  24 100       51  
  24 100       169  
  18 100       101  
  18 100       437  
  3 50       14  
  0 50       0  
  3 50       36  
  15 50       65  
  15 50       115  
  9         6922  
  9         22  
  9         90  
  9         20  
  9         128  
  8         41  
  8         153  
  4         11  
  1         10  
  3         38  
  4         21  
  4         26  
  5         1464  
  5         14  
  5         42  
  5         10  
  5         126  
  3         17  
  3         35  
  2         7  
  0         0  
  2         29  
  1         6  
  1         7  
  1         31  
  1         3  
  1         11  
  1         2  
  1         3  
  1         5  
  1         4  
  1         6  
  0         0  
  1         11  
  0            
  0            
61             package Aspect::Hook;
62              
63             *$NAME = sub $PROTOTYPE {
64             # Is this a lexically scoped hook that has finished
65             goto &\$original if $MATCH_DISABLED;
66              
67             # Apply any runtime-specific context checks
68             my \$wantarray = wantarray;
69             local \$Aspect::POINT = bless {
70             type => 'before',
71             pointcut => \$pointcut,
72             original => \$original,
73             sub_name => \$name,
74             wantarray => \$wantarray,
75             args => \\\@_,
76             exception => \$\@, ### Not used (yet)
77             }, 'Aspect::Point';
78              
79             local \$_ = \$Aspect::POINT;
80             goto &\$original unless $MATCH_RUN;
81              
82             # Run the advice code
83             &\$code(\$_);
84              
85             # Shortcut if they set a return value
86             if ( exists \$_->{return_value} ) {
87             return \@{\$_->{return_value}} if \$wantarray;
88             return \$_->{return_value};
89             }
90              
91             # Proceed to the original function
92             \@_ = \$_->args; ### Superfluous?
93             goto &\$original;
94             };
95             END_PERL
96 47         330 $self->{installed}++;
97             }
98              
99             # If this will run lexical we don't need a descoping hook
100 46 100       330 return unless $lexical;
101              
102             # Return the lexical descoping hook.
103             # This MUST be stored and run at DESTROY-time by the
104             # parent object calling _install. This is less bullet-proof
105             # than the DESTROY-time self-executing blessed coderef
106 27     27   249 return sub { $out_of_scope = 1 };
  27         183  
107             }
108              
109             # Check for pointcut usage not supported by the advice type
110             sub _validate {
111 46     46   94 my $self = shift;
112 46         293 my $pointcut = $self->pointcut;
113              
114             # The method used by the Highest pointcut is incompatible
115             # with the goto optimisation used by the before() advice.
116 46 50       344 if ( $pointcut->match_contains('Aspect::Pointcut::Highest') ) {
117 0         0 return 'The pointcut highest is not currently supported by before advice';
118             }
119              
120             # Pointcuts using "throwing" are irrelevant in before advice
121 46 50       164 if ( $pointcut->match_contains('Aspect::Pointcut::Throwing') ) {
122 0         0 return 'The pointcut throwing is illegal when used by before advice';
123             }
124              
125             # Pointcuts using "throwing" are irrelevant in before advice
126 46 100       166 if ( $pointcut->match_contains('Aspect::Pointcut::Returning') ) {
127 0         0 return 'The pointcut returning is illegal when used by before advice';
128             }
129              
130 46         340 $self->SUPER::_validate(@_);
131             }
132              
133             1;
134              
135             __END__