File Coverage

blib/lib/Aspect/Advice/Before.pm
Criterion Covered Total %
statement 80 88 90.9
branch 35 40 87.5
condition n/a
subroutine 13 13 100.0
pod n/a
total 128 141 90.7


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