Skip Menu |

This queue is for tickets about the Class-Method-Modifiers CPAN distribution.

Report information
The Basics
Id: 42510
Status: resolved
Priority: 0/
Queue: Class-Method-Modifiers

People
Owner: Nobody in particular
Requestors: gfuji [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: (no value)
Fixed in: (no value)



Subject: Performance improvement
Hello, Because method modifiers are really useful, I have written its XS implementation in Data::Util, which is faster than the current implementation (v1.01), especially in "before" and "after" modifiers. Moreover, even if no C compiler is available, Data::Util works currectly because it has also a Pure Perl impementation. I have attached a patch using Data::Util, requiring Data::Util 0.43. All the tests are passed, except for t/002-cache.t, which deals with implementation details. Regards, -- Goro Fuji (gfx) GFUJI at CPAN.org
Subject: Modifiers.diff
--- Modifiers.pm.orig 2008-06-27 11:22:48.000000000 +0900 +++ Modifiers.pm 2009-01-19 14:05:46.285500800 +0900 @@ -3,119 +3,45 @@ use strict; use warnings; -use MRO::Compat; - our $VERSION = '1.01'; -use base 'Exporter'; +use Exporter qw(import); our @EXPORT = qw(before after around); our @EXPORT_OK = @EXPORT; our %EXPORT_TAGS = ( - moose => [qw(before after around)], + moose => \@EXPORT, all => \@EXPORT_OK, ); use Carp 'confess'; -our %MODIFIER_CACHE; +use Data::Util; sub _install_modifier { my $into = shift; my $type = shift; my $code = pop; - my @names = @_; - - for my $name (@names) { - my $hit = $into->can($name) - or confess "The method '$name' is not found in the inheritance hierarchy for class $into"; - - my $qualified = $into.'::'.$name; - my $cache = $MODIFIER_CACHE{$into}{$name} ||= { - before => [], - after => [], - around => [], - }; - # this must be the first modifier we're installing - if (!exists($MODIFIER_CACHE{$into}{$name}{"orig"})) { - no strict 'refs'; - - # grab the original method (or undef if the method is inherited) - $cache->{"orig"} = *{$qualified}{CODE}; - - # the "innermost" method, the one that "around" will ultimately wrap - $cache->{"wrapped"} = $cache->{"orig"} || $hit; #sub { - # # we can't cache this, because new methods or modifiers may be - # # added between now and when this method is called - # for my $package (@{ mro::get_linear_isa($into) }) { - # next if $package eq $into; - # my $code = *{$package.'::'.$name}{CODE}; - # goto $code if $code; - # } - # confess "$qualified\::$name disappeared?"; - #}; - } - - # keep these lists in the order the modifiers are called - if ($type eq 'after') { - push @{ $cache->{$type} }, $code; - } - else { - unshift @{ $cache->{$type} }, $code; - } + foreach my $name(@_){ + my $hit = Data::Util::get_code_ref($into, $name); - # wrap the method with another layer of around. much simpler than - # the Moose equivalent. :) - if ($type eq 'around') { - my $method = $cache->{wrapped}; - $cache->{wrapped} = sub { - $code->($method, @_); - }; - } + if(!$hit || !Data::Util::subroutine_modifier($hit)){ - # install our new method which dispatches the modifiers, but only - # if a new type was added - if (@{ $cache->{$type} } == 1) { - - # avoid these hash lookups every method invocation - my $before = $cache->{"before"}; - my $after = $cache->{"after"}; - - # this is a coderef that changes every new "around". so we need - # to take a reference to it. better a deref than a hash lookup - my $wrapped = \$cache->{"wrapped"}; - - my $generated = 'sub {'; - - # before is easy, it doesn't affect the return value(s) - $generated .= '$_->(@_) for @$before;' if @$before; - - if (@$after) { - $generated .= ' - my @ret; - if (wantarray) { - @ret = $$wrapped->(@_); - } - else { - $ret[0] = $$wrapped->(@_); + unless($hit){ + $hit = $into->can($name) + or confess "The method '$name' is not found in the inheritance hierarchy for class $into"; } - $_->(@_) for @$after; + $hit = Data::Util::modify_subroutine($hit, $type => [$code]); - return wantarray ? @ret : $ret[0]; - '; + no warnings 'redefine'; + Data::Util::install_subroutine($into, $name => $hit); } - else { - $generated .= '$$wrapped->(@_);'; + else{ # $hit exists and is already modified + Data::Util::subroutine_modifier($hit, $type => $code); } - - $generated .= '}'; - - no strict 'refs'; - no warnings 'redefine'; - *$qualified = eval $generated; - }; } + return; } sub before {
This was implemented by a third party as Class::Method::Modifiers::Fast.