X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2F_Util.pm;h=f6e04fe055e0684f0e1ca47d1f70504a2a650d09;hb=bb768302f9abecbd4a32090ba38d5938a009bd7b;hp=bb06ec2cb3a984f0c77bec711dbac6dea7def42e;hpb=3b0202245e84a09a41ac31a13b80547a300a227e;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/_Util.pm b/lib/DBIx/Class/_Util.pm index bb06ec2..f6e04fe 100644 --- a/lib/DBIx/Class/_Util.pm +++ b/lib/DBIx/Class/_Util.pm @@ -71,44 +71,53 @@ BEGIN { ( $mro_recursor_stack->{cache} || {} )->{$_}{methlist} ||= do { my $class = $_; - no strict 'refs'; - my %methlist = + + # RV to be hashed up and turned into a number + join "\0", ( + $class, map - # this is essentially a uniq_by step - # it is crucial on OLD_MRO - {( Scalar::Util::refaddr($_) => $_ )} + {( + # stringification should be sufficient, ignore names/refaddr entirely + $_, + attributes::get( $_ ), + )} map - { + {( + # skip dummy C::C3 helper crefs + ! ( ( $Class::C3::MRO{$class} || {} )->{methods}{$_} ) + and ( ref(\ "${class}::"->{$_} ) ne 'GLOB' or defined( *{ "${class}::"->{$_} }{CODE} ) ) + ) ? ( \&{"${class}::$_"} ) : () } keys %{ "${class}::" } - ; - - # RV to be hashed up and turned into a number - join "\0", ( - $class, - map {( - $_, # refaddr is sufficient, ignore names entirely - @{ - ( $mro_recursor_stack->{cache} || {} )->{attrs}{$_} - ||= - [ attributes::get( $methlist{$_} ) ] - }, - )} sort keys %methlist - ), + ); } - } ( 'UNIVERSAL', @{ - ( $mro_recursor_stack->{cache} || {} )->{$_[0]}{linear_isa} - ||= - mro::get_linear_isa($_[0]) - } ) ) ) ) + } ( + + @{ + ( $mro_recursor_stack->{cache} || {} )->{$_[0]}{linear_isa} + ||= + mro::get_linear_isa($_[0]) + }, + + (( + ( $mro_recursor_stack->{cache} || {} )->{$_[0]}{is_universal} + ||= + mro::is_universal($_[0]) + ) ? () : @{ + ( $mro_recursor_stack->{cache} || {} )->{UNIVERSAL}{linear_isa} + ||= + mro::get_linear_isa("UNIVERSAL") + } ), + + ) ) ) ) ); }; } @@ -644,9 +653,21 @@ sub modver_gt_or_eq_and_lt ($$$) { our $describe_class_query_cache; sub describe_class_methods { + my ($class, $requested_mro) = @_; croak "Expecting a class name" - if not defined $_[0] or $_[0] !~ $module_name_rx; + if not defined $class or $class !~ $module_name_rx; + + $requested_mro ||= mro::get_mro($class); + + # mro::set_mro() does not bump pkg_gen - WHAT THE FUCK?! + my $query_cache_key = "$class|$requested_mro"; + + my $stack_cache_key = + ( mro::get_mro($class) eq $requested_mro ) + ? $class + : $query_cache_key + ; # use a cache on old MRO, since while we are recursing in this function # nothing can possibly change (the speedup is immense) @@ -661,82 +682,66 @@ sub modver_gt_or_eq_and_lt ($$$) { my $my_gen = 0; - $my_gen += get_real_pkg_gen($_) for ( - 'UNIVERSAL', - my ($class, @my_ISA) = @{ - $mro_recursor_stack->{cache}{$_[0]}{linear_isa} + $my_gen += get_real_pkg_gen($_) for ( my @full_ISA = ( + + @{ + $mro_recursor_stack->{cache}{$stack_cache_key}{linear_isa} ||= - mro::get_linear_isa($_[0]) - } - ); + mro::get_linear_isa($class, $requested_mro) + }, - my $slot = $describe_class_query_cache->{$class} ||= {}; + (( + $mro_recursor_stack->{cache}{$class}{is_universal} + ||= + mro::is_universal($class) + ) ? () : @{ + $mro_recursor_stack->{cache}{UNIVERSAL}{linear_isa} + ||= + mro::get_linear_isa("UNIVERSAL") + }), + + )); + + my $slot = $describe_class_query_cache->{$query_cache_key} ||= {}; unless ( ($slot->{cumulative_gen}||0) == $my_gen ) { + # remove ourselves from ISA + shift @full_ISA; + # reset %$slot = ( class => $class, - isa => [ @my_ISA ], # copy before we shove UNIVERSAL into it + isa => [ + @{ $mro_recursor_stack->{cache}{$stack_cache_key}{linear_isa} } + [ 1 .. $#{$mro_recursor_stack->{cache}{$stack_cache_key}{linear_isa}} ] + ], mro => { - type => mro::get_mro($class), + type => $requested_mro, + is_c3 => ( ($requested_mro eq 'c3') ? 1 : 0 ), }, cumulative_gen => $my_gen, ); - $slot->{mro}{is_c3} = ($slot->{mro}{type} eq 'c3') ? 1 : 0; - - push @my_ISA, 'UNIVERSAL'; # ensure the cache is populated for the parents, code below can then # efficiently operate over the query_cache directly - for (reverse @my_ISA) { - my ($parent_gen, @parent_ISA); - - # and even more skips before calling out recursively - describe_class_methods($_) unless ( - $describe_class_query_cache->{$_}{cumulative_gen} - and - $parent_gen = get_real_pkg_gen($_) - and - ( - ( - (undef, @parent_ISA) = @{ - $mro_recursor_stack->{cache}{$_}{linear_isa} - ||= - mro::get_linear_isa($_) - } - ) == 1 - or - do { - $parent_gen += get_real_pkg_gen($_) for @parent_ISA; - 1; - } - ) - and - $describe_class_query_cache->{$_}{cumulative_gen} == $parent_gen - ); - } + describe_class_methods($_) for reverse @full_ISA; - my ($methods_seen_via_ISA_on_old_mro, $current_node_refaddr); no strict 'refs'; # combine full ISA-order inherited and local method list into a # "shadowing stack" ( - $current_node_refaddr = refaddr($_) - - and - - # on complex MI herarchies the method can be anywhere in the - # shadow stack - look through the entire slot, not just [0] - ( ! grep { - refaddr($_) == $current_node_refaddr - } @{ $slot->{methods}{ $_->{name} } || [] } ) + unshift @{ $slot->{methods}{$_->{name}} }, $_ and - unshift @{ $slot->{methods}{$_->{name}} }, $_ + ( + $_->{via_class} ne $class + or + $slot->{methods_defined_in_class}{$_->{name}} = $_ + ) and @@ -748,50 +753,33 @@ sub modver_gt_or_eq_and_lt ($$$) { ) for ( - # what describe_class_methods for @my_ISA produced above - ( map { $_->[0] } map { - values %{ $describe_class_query_cache->{$_}{methods} } - } reverse @my_ISA ), + # what describe_class_methods for @full_ISA produced above + ( map { values %{ + $describe_class_query_cache->{$_}{methods_defined_in_class} || {} + } } map { "$_|" . mro::get_mro($_) } reverse @full_ISA ), # our own non-cleaned subs + their attributes ( map { ( - # these 2 OR-ed checks are sufficient for 5.10+ + # need to account for dummy helper crefs under OLD_MRO ( - ref(\ "${class}::"->{$_} ) ne 'GLOB' + ! DBIx::Class::_ENV_::OLD_MRO or - defined( *{ "${class}::"->{$_} }{CODE} ) + ! ( ( $Class::C3::MRO{$class} || {} )->{methods}{$_} ) ) and - # need to account for dummy helper crefs under OLD_MRO + # these 2 OR-ed checks are sufficient for 5.10+ ( - ! DBIx::Class::_ENV_::OLD_MRO + ref(\ "${class}::"->{$_} ) ne 'GLOB' or - ( - $methods_seen_via_ISA_on_old_mro ||= do { - my $rv = {}; - $rv->{$_->{name}}->{ refaddr( \&{ "$_->{via_class}::$_->{name}"} ) } = 1 for - map { @$_ } map - { values %{ $describe_class_query_cache->{$_}{methods} } } - @my_ISA; - $rv; - } - and - ( - ! $methods_seen_via_ISA_on_old_mro->{$_} - or - ! $methods_seen_via_ISA_on_old_mro->{$_}{ refaddr( \&{"${class}::${_}"} ) } - ) - ) + defined( *{ "${class}::"->{$_} }{CODE} ) ) ) ? { via_class => $class, name => $_, - attributes => { map { $_ => 1 } @{ - $mro_recursor_stack->{cache}{attrs}{ refaddr \&{"${class}::${_}"} } - ||= - [ attributes::get( \&{"${class}::${_}"} ) ] - } }, + attributes => { + map { $_ => 1 } attributes::get( \&{"${class}::${_}"} ) + }, } : () } keys %{"${class}::"} ) @@ -810,7 +798,7 @@ sub modver_gt_or_eq_and_lt ($$$) { $slot->{cumulative_gen} = 0; $slot->{cumulative_gen} += get_real_pkg_gen($_) - for $class, @my_ISA; + for $class, @full_ISA; } }