| Filename | /Users/ether/.perlbrew/libs/36.0@std/lib/perl5/Class/Method/Modifiers.pm |
| Statements | Executed 1119 statements in 7.08ms |
| Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
|---|---|---|---|---|---|
| 19 | 2 | 2 | 3.66ms | 5.87ms | Class::Method::Modifiers::install_modifier |
| 47 | 2 | 1 | 2.16ms | 2.16ms | Class::Method::Modifiers::_sub_attrs |
| 1 | 1 | 1 | 48µs | 53µs | Moo::_Utils::BEGIN@1 |
| 2 | 2 | 2 | 14µs | 1.04ms | Class::Method::Modifiers::around |
| 1 | 1 | 1 | 12µs | 86µs | Class::Method::Modifiers::BEGIN@10 |
| 1 | 1 | 1 | 10µs | 55µs | Moo::_Utils::BEGIN@2.27 |
| 1 | 1 | 1 | 9µs | 13µs | Class::Method::Modifiers::BEGIN@196 |
| 1 | 1 | 1 | 8µs | 18µs | Class::Method::Modifiers::BEGIN@54 |
| 1 | 1 | 1 | 7µs | 11µs | Class::Method::Modifiers::BEGIN@145 |
| 1 | 1 | 1 | 6µs | 25µs | Class::Method::Modifiers::BEGIN@146 |
| 1 | 1 | 1 | 6µs | 14µs | Class::Method::Modifiers::BEGIN@147 |
| 1 | 1 | 1 | 6µs | 18µs | Class::Method::Modifiers::BEGIN@200 |
| 1 | 1 | 1 | 5µs | 5µs | Class::Method::Modifiers::BEGIN@19 |
| 0 | 0 | 0 | 0s | 0s | Class::Method::Modifiers::_fresh |
| 0 | 0 | 0 | 0s | 0s | Class::Method::Modifiers::_is_in_package |
| 0 | 0 | 0 | 0s | 0s | Class::Method::Modifiers::after |
| 0 | 0 | 0 | 0s | 0s | Class::Method::Modifiers::before |
| 0 | 0 | 0 | 0s | 0s | Class::Method::Modifiers::fresh |
| Line | State ments |
Time on line |
Calls | Time in subs |
Code |
|---|---|---|---|---|---|
| 1 | 2 | 51µs | 2 | 58µs | # spent 53µs (48+5) within Moo::_Utils::BEGIN@1 which was called:
# once (48µs+5µs) by Moo::_Utils::_install_modifier at line 1 # spent 53µs making 1 call to Moo::_Utils::BEGIN@1
# spent 5µs making 1 call to strict::import |
| 2 | 2 | 56µs | 2 | 100µs | # spent 55µs (10+45) within Moo::_Utils::BEGIN@2.27 which was called:
# once (10µs+45µs) by Moo::_Utils::_install_modifier at line 2 # spent 55µs making 1 call to Moo::_Utils::BEGIN@2.27
# spent 45µs making 1 call to warnings::import |
| 3 | package Class::Method::Modifiers; # git description: v2.12-17-gbc38636 | ||||
| 4 | # ABSTRACT: Provides Moose-like method modifiers | ||||
| 5 | # KEYWORDS: method wrap modification patch | ||||
| 6 | # vim: set ts=8 sts=4 sw=4 tw=115 et : | ||||
| 7 | |||||
| 8 | 1 | 0s | our $VERSION = '2.13'; | ||
| 9 | |||||
| 10 | 2 | 108µs | 2 | 160µs | # spent 86µs (12+74) within Class::Method::Modifiers::BEGIN@10 which was called:
# once (12µs+74µs) by Moo::_Utils::_install_modifier at line 10 # spent 86µs making 1 call to Class::Method::Modifiers::BEGIN@10
# spent 74µs making 1 call to base::import |
| 11 | |||||
| 12 | 1 | 1µs | our @EXPORT = qw(before after around); | ||
| 13 | 1 | 0s | our @EXPORT_OK = (@EXPORT, qw(fresh install_modifier)); | ||
| 14 | 1 | 3µs | our %EXPORT_TAGS = ( | ||
| 15 | moose => [qw(before after around)], | ||||
| 16 | all => \@EXPORT_OK, | ||||
| 17 | ); | ||||
| 18 | |||||
| 19 | # spent 5µs within Class::Method::Modifiers::BEGIN@19 which was called:
# once (5µs+0s) by Moo::_Utils::_install_modifier at line 21 | ||||
| 20 | 1 | 5µs | *_HAS_READONLY = $] >= 5.008 ? sub(){1} : sub(){0}; | ||
| 21 | 1 | 185µs | 1 | 5µs | } # spent 5µs making 1 call to Class::Method::Modifiers::BEGIN@19 |
| 22 | |||||
| 23 | our %MODIFIER_CACHE; | ||||
| 24 | |||||
| 25 | # for backward compatibility | ||||
| 26 | sub _install_modifier; # -w | ||||
| 27 | 1 | 2µs | *_install_modifier = \&install_modifier; | ||
| 28 | |||||
| 29 | # spent 5.87ms (3.66+2.21) within Class::Method::Modifiers::install_modifier which was called 19 times, avg 309µs/call:
# 17 times (3.15ms+1.69ms) by Moo::_Utils::_install_modifier at line 86 of Moo/_Utils.pm, avg 285µs/call
# 2 times (513µs+514µs) by Class::Method::Modifiers::around at line 162, avg 514µs/call | ||||
| 30 | 19 | 8µs | my $into = shift; | ||
| 31 | 19 | 11µs | my $type = shift; | ||
| 32 | 19 | 9µs | my $code = pop; | ||
| 33 | 19 | 7µs | my @names = @_; | ||
| 34 | |||||
| 35 | 19 | 15µs | @names = @{ $names[0] } if ref($names[0]) eq 'ARRAY'; | ||
| 36 | |||||
| 37 | 19 | 6µs | return _fresh($into, $code, @names) if $type eq 'fresh'; | ||
| 38 | |||||
| 39 | 19 | 73µs | for my $name (@names) { | ||
| 40 | 35 | 204µs | 35 | 43µs | my $hit = $into->can($name) or do { # spent 43µs making 35 calls to UNIVERSAL::can, avg 1µs/call |
| 41 | require Carp; | ||||
| 42 | Carp::confess("The method '$name' is not found in the inheritance hierarchy for class $into"); | ||||
| 43 | }; | ||||
| 44 | |||||
| 45 | 35 | 21µs | my $qualified = $into.'::'.$name; | ||
| 46 | 35 | 87µs | my $cache = $MODIFIER_CACHE{$into}{$name} ||= { | ||
| 47 | before => [], | ||||
| 48 | after => [], | ||||
| 49 | around => [], | ||||
| 50 | }; | ||||
| 51 | |||||
| 52 | # this must be the first modifier we're installing | ||||
| 53 | 35 | 17µs | if (!exists($cache->{"orig"})) { | ||
| 54 | 2 | 220µs | 2 | 28µs | # spent 18µs (8+10) within Class::Method::Modifiers::BEGIN@54 which was called:
# once (8µs+10µs) by Moo::_Utils::_install_modifier at line 54 # spent 18µs making 1 call to Class::Method::Modifiers::BEGIN@54
# spent 10µs making 1 call to strict::unimport |
| 55 | |||||
| 56 | # grab the original method (or undef if the method is inherited) | ||||
| 57 | 35 | 65µs | $cache->{"orig"} = *{$qualified}{CODE}; | ||
| 58 | |||||
| 59 | # the "innermost" method, the one that "around" will ultimately wrap | ||||
| 60 | 35 | 15µs | $cache->{"wrapped"} = $cache->{"orig"} || $hit; #sub { | ||
| 61 | # # we can't cache this, because new methods or modifiers may be | ||||
| 62 | # # added between now and when this method is called | ||||
| 63 | # for my $package (@{ mro::get_linear_isa($into) }) { | ||||
| 64 | # next if $package eq $into; | ||||
| 65 | # my $code = *{$package.'::'.$name}{CODE}; | ||||
| 66 | # goto $code if $code; | ||||
| 67 | # } | ||||
| 68 | # require Carp; | ||||
| 69 | # Carp::confess("$qualified\::$name disappeared?"); | ||||
| 70 | #}; | ||||
| 71 | } | ||||
| 72 | |||||
| 73 | # keep these lists in the order the modifiers are called | ||||
| 74 | 35 | 21µs | if ($type eq 'after') { | ||
| 75 | push @{ $cache->{$type} }, $code; | ||||
| 76 | } | ||||
| 77 | else { | ||||
| 78 | 35 | 25µs | unshift @{ $cache->{$type} }, $code; | ||
| 79 | } | ||||
| 80 | |||||
| 81 | # wrap the method with another layer of around. much simpler than | ||||
| 82 | # the Moose equivalent. :) | ||||
| 83 | 35 | 16µs | if ($type eq 'around') { | ||
| 84 | 12 | 7µs | my $method = $cache->{wrapped}; | ||
| 85 | 12 | 28µs | 12 | 585µs | my $attrs = _sub_attrs($code); # spent 585µs making 12 calls to Class::Method::Modifiers::_sub_attrs, avg 49µs/call |
| 86 | # a bare "sub :lvalue {...}" will be parsed as a label and an | ||||
| 87 | # indirect method call. force it to be treated as an expression | ||||
| 88 | # using + | ||||
| 89 | 12 | 639µs | $cache->{wrapped} = eval "package $into; +sub $attrs { \$code->(\$method, \@_); };"; # spent 265ms executing statements in 3 string evals (merged) # includes 263ms spent executing 73713 calls to 1 sub defined therein. # spent 298µs executing statements in 2 string evals (merged) # includes 324µs spent executing 88 calls to 1 sub defined therein. # spent 113µs executing statements in string eval # includes 129µs spent executing 22 calls to 1 sub defined therein. # spent 66µs executing statements in 3 string evals (merged) # includes 146µs spent executing 16 calls to 1 sub defined therein. # spent 9µs executing statements in string eval # includes 7µs spent executing 1 call to 1 sub defined therein. # spent 5µs executing statements in string eval # spent 3µs executing statements in string eval | ||
| 90 | } | ||||
| 91 | |||||
| 92 | # install our new method which dispatches the modifiers, but only | ||||
| 93 | # if a new type was added | ||||
| 94 | 35 | 51µs | if (@{ $cache->{$type} } == 1) { | ||
| 95 | |||||
| 96 | # avoid these hash lookups every method invocation | ||||
| 97 | 35 | 8µs | my $before = $cache->{"before"}; | ||
| 98 | 35 | 5µs | my $after = $cache->{"after"}; | ||
| 99 | |||||
| 100 | # this is a coderef that changes every new "around". so we need | ||||
| 101 | # to take a reference to it. better a deref than a hash lookup | ||||
| 102 | 35 | 11µs | my $wrapped = \$cache->{"wrapped"}; | ||
| 103 | |||||
| 104 | 35 | 102µs | 35 | 1.58ms | my $attrs = _sub_attrs($cache->{wrapped}); # spent 1.58ms making 35 calls to Class::Method::Modifiers::_sub_attrs, avg 45µs/call |
| 105 | |||||
| 106 | 35 | 17µs | my $generated = "package $into;\n"; | ||
| 107 | 35 | 13µs | $generated .= "sub $name $attrs {"; | ||
| 108 | |||||
| 109 | # before is easy, it doesn't affect the return value(s) | ||||
| 110 | 35 | 22µs | if (@$before) { | ||
| 111 | $generated .= ' | ||||
| 112 | for my $method (@$before) { | ||||
| 113 | $method->(@_); | ||||
| 114 | } | ||||
| 115 | '; | ||||
| 116 | } | ||||
| 117 | |||||
| 118 | 35 | 16µs | if (@$after) { | ||
| 119 | $generated .= ' | ||||
| 120 | my $ret; | ||||
| 121 | if (wantarray) { | ||||
| 122 | $ret = [$$wrapped->(@_)]; | ||||
| 123 | '.(_HAS_READONLY ? 'Internals::SvREADONLY(@$ret, 1);' : '').' | ||||
| 124 | } | ||||
| 125 | elsif (defined wantarray) { | ||||
| 126 | $ret = \($$wrapped->(@_)); | ||||
| 127 | } | ||||
| 128 | else { | ||||
| 129 | $$wrapped->(@_); | ||||
| 130 | } | ||||
| 131 | |||||
| 132 | for my $method (@$after) { | ||||
| 133 | $method->(@_); | ||||
| 134 | } | ||||
| 135 | |||||
| 136 | wantarray ? @$ret : $ret ? $$ret : (); | ||||
| 137 | ' | ||||
| 138 | } | ||||
| 139 | else { | ||||
| 140 | 35 | 66µs | $generated .= '$$wrapped->(@_);'; | ||
| 141 | } | ||||
| 142 | |||||
| 143 | 35 | 4µs | $generated .= '}'; | ||
| 144 | |||||
| 145 | 2 | 29µs | 2 | 15µs | # spent 11µs (7+4) within Class::Method::Modifiers::BEGIN@145 which was called:
# once (7µs+4µs) by Moo::_Utils::_install_modifier at line 145 # spent 11µs making 1 call to Class::Method::Modifiers::BEGIN@145
# spent 4µs making 1 call to strict::unimport |
| 146 | 2 | 19µs | 2 | 44µs | # spent 25µs (6+19) within Class::Method::Modifiers::BEGIN@146 which was called:
# once (6µs+19µs) by Moo::_Utils::_install_modifier at line 146 # spent 25µs making 1 call to Class::Method::Modifiers::BEGIN@146
# spent 19µs making 1 call to warnings::unimport |
| 147 | 2 | 308µs | 2 | 22µs | # spent 14µs (6+8) within Class::Method::Modifiers::BEGIN@147 which was called:
# once (6µs+8µs) by Moo::_Utils::_install_modifier at line 147 # spent 14µs making 1 call to Class::Method::Modifiers::BEGIN@147
# spent 8µs making 1 call to warnings::unimport |
| 148 | 35 | 1.98ms | eval $generated; # spent 233ms executing statements in string eval # includes 238ms spent executing 73648 calls to 1 sub defined therein. # spent 263µs executing statements in string eval # includes 170µs spent executing 65 calls to 1 sub defined therein. # spent 214µs executing statements in string eval # includes 245µs spent executing 68 calls to 1 sub defined therein. # spent 210µs executing statements in string eval # includes 173µs spent executing 16 calls to 1 sub defined therein. # spent 81µs executing statements in string eval # includes 69µs spent executing 20 calls to 1 sub defined therein. # spent 77µs executing statements in string eval # includes 165µs spent executing 33 calls to 1 sub defined therein. # spent 76µs executing statements in string eval # includes 342µs spent executing 58 calls to 1 sub defined therein. # spent 68µs executing statements in string eval # includes 91µs spent executing 8 calls to 1 sub defined therein. # spent 54µs executing statements in string eval # includes 65µs spent executing 6 calls to 1 sub defined therein. # spent 38µs executing statements in string eval # includes 47µs spent executing 5 calls to 1 sub defined therein. # spent 37µs executing statements in string eval # includes 48µs spent executing 4 calls to 1 sub defined therein. # spent 20µs executing statements in string eval # includes 14µs spent executing 2 calls to 1 sub defined therein. # spent 17µs executing statements in string eval # includes 20µs spent executing 4 calls to 1 sub defined therein. # spent 12µs executing statements in string eval # includes 30µs spent executing 2 calls to 1 sub defined therein. # spent 12µs executing statements in string eval # includes 13µs spent executing 1 call to 1 sub defined therein. # spent 10µs executing statements in string eval # includes 11µs spent executing 1 call to 1 sub defined therein. # spent 5µs executing statements in string eval # includes 5µs spent executing 1 call to 1 sub defined therein. # spent 4µs executing statements in string eval # includes 4µs spent executing 1 call to 1 sub defined therein. # spent 0s executing statements in string eval | ||
| 149 | }; | ||||
| 150 | } | ||||
| 151 | } | ||||
| 152 | |||||
| 153 | sub before { | ||||
| 154 | _install_modifier(scalar(caller), 'before', @_); | ||||
| 155 | } | ||||
| 156 | |||||
| 157 | sub after { | ||||
| 158 | _install_modifier(scalar(caller), 'after', @_); | ||||
| 159 | } | ||||
| 160 | |||||
| 161 | # spent 1.04ms (14µs+1.03) within Class::Method::Modifiers::around which was called 2 times, avg 520µs/call:
# once (6µs+736µs) by Module::Runtime::require_module at line 22 of Data/Perl/Collection/Hash/MooseLike.pm
# once (8µs+291µs) by Module::Runtime::require_module at line 20 of Data/Perl/Collection/Array/MooseLike.pm | ||||
| 162 | 2 | 13µs | 2 | 1.03ms | _install_modifier(scalar(caller), 'around', @_); # spent 1.03ms making 2 calls to Class::Method::Modifiers::install_modifier, avg 514µs/call |
| 163 | } | ||||
| 164 | |||||
| 165 | sub fresh { | ||||
| 166 | my $code = pop; | ||||
| 167 | my @names = @_; | ||||
| 168 | |||||
| 169 | @names = @{ $names[0] } if ref($names[0]) eq 'ARRAY'; | ||||
| 170 | |||||
| 171 | _fresh(scalar(caller), $code, @names); | ||||
| 172 | } | ||||
| 173 | |||||
| 174 | sub _fresh { | ||||
| 175 | my ($into, $code, @names) = @_; | ||||
| 176 | |||||
| 177 | for my $name (@names) { | ||||
| 178 | if ($name !~ /\A [a-zA-Z_] [a-zA-Z0-9_]* \z/xms) { | ||||
| 179 | require Carp; | ||||
| 180 | Carp::confess("Invalid method name '$name'"); | ||||
| 181 | } | ||||
| 182 | if ($into->can($name)) { | ||||
| 183 | require Carp; | ||||
| 184 | Carp::confess("Class $into already has a method named '$name'"); | ||||
| 185 | } | ||||
| 186 | |||||
| 187 | # We need to make sure that the installed method has its CvNAME in | ||||
| 188 | # the appropriate package; otherwise, it would be subject to | ||||
| 189 | # deletion if callers use namespace::autoclean. If $code was | ||||
| 190 | # compiled in the target package, we can just install it directly; | ||||
| 191 | # otherwise, we'll need a different approach. Using Sub::Name would | ||||
| 192 | # be fine in all cases, at the cost of introducing a dependency on | ||||
| 193 | # an XS-using, non-core module. So instead we'll use string-eval to | ||||
| 194 | # create a new subroutine that wraps $code. | ||||
| 195 | if (_is_in_package($code, $into)) { | ||||
| 196 | 2 | 39µs | 2 | 17µs | # spent 13µs (9+4) within Class::Method::Modifiers::BEGIN@196 which was called:
# once (9µs+4µs) by Moo::_Utils::_install_modifier at line 196 # spent 13µs making 1 call to Class::Method::Modifiers::BEGIN@196
# spent 4µs making 1 call to strict::unimport |
| 197 | *{"$into\::$name"} = $code; | ||||
| 198 | } | ||||
| 199 | else { | ||||
| 200 | 2 | 216µs | 2 | 30µs | # spent 18µs (6+12) within Class::Method::Modifiers::BEGIN@200 which was called:
# once (6µs+12µs) by Moo::_Utils::_install_modifier at line 200 # spent 18µs making 1 call to Class::Method::Modifiers::BEGIN@200
# spent 12µs making 1 call to warnings::unimport |
| 201 | my $attrs = _sub_attrs($code); | ||||
| 202 | eval "package $into; sub $name $attrs { \$code->(\@_) }"; | ||||
| 203 | } | ||||
| 204 | } | ||||
| 205 | } | ||||
| 206 | |||||
| 207 | sub _sub_attrs { | ||||
| 208 | 47 | 10µs | my ($coderef) = @_; | ||
| 209 | 47 | 38µs | local *_sub = $coderef; | ||
| 210 | 47 | 9µs | local $@; | ||
| 211 | # this assignment will fail to compile if it isn't an lvalue sub. we | ||||
| 212 | # never want to actually call the sub though, so we return early. | ||||
| 213 | 47 | 2.19ms | (eval 'return 1; &_sub = 1') ? ':lvalue' : ''; | ||
| 214 | } | ||||
| 215 | |||||
| 216 | sub _is_in_package { | ||||
| 217 | my ($coderef, $package) = @_; | ||||
| 218 | require B; | ||||
| 219 | my $cv = B::svref_2object($coderef); | ||||
| 220 | return $cv->GV->STASH->NAME eq $package; | ||||
| 221 | } | ||||
| 222 | |||||
| 223 | 1 | 7µs | 1; | ||
| 224 | |||||
| 225 | __END__ |