Add a get_subname to _Util
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / _Util.pm
1 package # hide from PAUSE
2   DBIx::Class::_Util;
3
4 use DBIx::Class::StartupCheck;  # load es early as we can, usually a noop
5
6 use warnings;
7 use strict;
8
9 # For the love of everything that is crab-like: DO NOT reach into this
10 # The entire thing is really fragile and should not be screwed with
11 # unless absolutely and unavoidably necessary
12 our $__describe_class_query_cache;
13
14 BEGIN {
15   package # hide from pause
16     DBIx::Class::_ENV_;
17
18   use Config;
19
20   use constant {
21     PERL_VERSION => "$]",
22     OS_NAME => "$^O",
23   };
24
25   use constant {
26
27     # but of course
28     BROKEN_FORK => (OS_NAME eq 'MSWin32') ? 1 : 0,
29
30     BROKEN_GOTO => ( PERL_VERSION < 5.008003 ) ? 1 : 0,
31
32     # perl -MScalar::Util=weaken -e 'weaken( $hash{key} = \"value" )'
33     BROKEN_WEAK_SCALARREF_VALUES => ( PERL_VERSION < 5.008003 ) ? 1 : 0,
34
35     HAS_ITHREADS => $Config{useithreads} ? 1 : 0,
36
37     UNSTABLE_DOLLARAT => ( PERL_VERSION < 5.013002 ) ? 1 : 0,
38
39     ( map
40       #
41       # the "DBIC_" prefix below is crucial - this is what makes CI pick up
42       # all envvars without further adjusting its scripts
43       # DO NOT CHANGE to the more logical { $_ => !!( $ENV{"DBIC_$_"} ) }
44       #
45       { substr($_, 5) => !!( $ENV{$_} ) }
46       qw(
47         DBIC_SHUFFLE_UNORDERED_RESULTSETS
48         DBIC_ASSERT_NO_INTERNAL_WANTARRAY
49         DBIC_ASSERT_NO_INTERNAL_INDIRECT_CALLS
50         DBIC_STRESSTEST_UTF8_UPGRADE_GENERATED_COLLAPSER_SOURCE
51         DBIC_STRESSTEST_COLUMN_INFO_UNAWARE_STORAGE
52       )
53     ),
54
55     IV_SIZE => $Config{ivsize},
56   };
57
58   if ( PERL_VERSION < 5.009_005) {
59     require MRO::Compat;
60     constant->import( OLD_MRO => 1 );
61
62     #
63     # Yes, I know this is a rather PHP-ish name, but please first read
64     # https://metacpan.org/source/BOBTFISH/MRO-Compat-0.12/lib/MRO/Compat.pm#L363-368
65     #
66     # Even if we are using Class::C3::XS it still won't work, as doing
67     #   defined( *{ "SubClass::"->{$_} }{CODE} )
68     # will set pkg_gen to the same value for SubClass and *ALL PARENTS*
69     #
70     *DBIx::Class::_Util::get_real_pkg_gen = sub ($) {
71       require Digest::MD5;
72       require Math::BigInt;
73
74       my $cur_class;
75       no strict 'refs';
76
77       # the non-assign-unless-there-is-a-hash is deliberate
78       ( $__describe_class_query_cache->{'!internal!'} || {} )->{$_[0]}{gen} ||= (
79         Math::BigInt->new( '0x' . ( Digest::MD5::md5_hex( join "\0", map {
80
81           ( $__describe_class_query_cache->{'!internal!'} || {} )->{$_}{methlist} ||= (
82
83             $cur_class = $_
84
85               and
86
87             # RV to be hashed up and turned into a number
88             join "\0", (
89               $cur_class,
90               map
91                 {(
92                   # stringification should be sufficient, ignore names/refaddr entirely
93                   $_,
94                   do {
95                     my @attrs;
96                     local $@;
97                     local $SIG{__DIE__} if $SIG{__DIE__};
98                     # attributes::get may throw on blessed-false crefs :/
99                     eval { @attrs = attributes::get( $_ ); 1 }
100                       or warn "Unable to determine attributes of coderef $_ due to the following error: $@";
101                     @attrs;
102                   },
103                 )}
104                 map
105                   {(
106                     # skip dummy C::C3 helper crefs
107                     ! ( ( $Class::C3::MRO{$cur_class} || {} )->{methods}{$_} )
108                       and
109                     (
110                       ref(\ "${cur_class}::"->{$_} ) ne 'GLOB'
111                         or
112                       defined( *{ "${cur_class}::"->{$_} }{CODE} )
113                     )
114                   )
115                     ? ( \&{"${cur_class}::$_"} )
116                     : ()
117                   }
118                   keys %{ "${cur_class}::" }
119             )
120           )
121         } (
122
123           @{
124             ( $__describe_class_query_cache->{'!internal!'} || {} )->{$_[0]}{linear_isa}
125               ||=
126             mro::get_linear_isa($_[0])
127           },
128
129           ((
130             ( $__describe_class_query_cache->{'!internal!'} || {} )->{$_[0]}{is_universal}
131               ||=
132             mro::is_universal($_[0])
133           ) ? () : @{
134             ( $__describe_class_query_cache->{'!internal!'} || {} )->{UNIVERSAL}{linear_isa}
135               ||=
136             mro::get_linear_isa("UNIVERSAL")
137           } ),
138
139         ) ) ) )
140       );
141     };
142   }
143   else {
144     require mro;
145     constant->import( OLD_MRO => 0 );
146     *DBIx::Class::_Util::get_real_pkg_gen = \&mro::get_pkg_gen;
147   }
148
149   # Both of these are no longer used for anything. However bring
150   # them back after they were purged in 08a8d8f1, as there appear
151   # to be outfits with *COPY PASTED* pieces of lib/DBIx/Class/Storage/*
152   # in their production codebases. There is no point in breaking these
153   # if whatever they used actually continues to work
154   my $sigh = sub {
155     DBIx::Class::_Util::emit_loud_diag(
156       skip_frames => 1,
157       msg => "The @{[ (caller(1))[3] ]} constant is no more - adjust your code"
158     );
159
160     0;
161   };
162   sub DBICTEST () { &$sigh }
163   sub PEEPEENESS () { &$sigh }
164 }
165
166 use constant SPURIOUS_VERSION_CHECK_WARNINGS => ( DBIx::Class::_ENV_::PERL_VERSION < 5.010 ? 1 : 0);
167
168 # FIXME - this is not supposed to be here
169 # Carp::Skip to the rescue soon
170 use DBIx::Class::Carp '^DBIx::Class|^DBICTest';
171
172 use B ();
173 use Carp 'croak';
174 use Storable 'nfreeze';
175 use Scalar::Util qw(weaken blessed reftype refaddr);
176 use Sub::Quote qw(qsub);
177 use Sub::Name ();
178 use attributes ();
179
180 # Already correctly prototyped: perlbrew exec perl -MStorable -e 'warn prototype \&Storable::dclone'
181 BEGIN { *deep_clone = \&Storable::dclone }
182
183 use base 'Exporter';
184 our @EXPORT_OK = qw(
185   sigwarn_silencer modver_gt_or_eq modver_gt_or_eq_and_lt
186   fail_on_internal_wantarray fail_on_internal_call
187   refdesc refcount hrefaddr set_subname get_subname describe_class_methods
188   scope_guard detected_reinvoked_destructor emit_loud_diag
189   true false
190   is_exception dbic_internal_try visit_namespaces
191   quote_sub qsub perlstring serialize deep_clone dump_value uniq
192   parent_dir mkdir_p
193   UNRESOLVABLE_CONDITION
194 );
195
196 use constant UNRESOLVABLE_CONDITION => \ '1 = 0';
197
198 # Override forcing no_defer, and adding naming consistency checks
199 our %refs_closed_over_by_quote_sub_installed_crefs;
200 sub quote_sub {
201   Carp::confess( "Anonymous quoting not supported by the DBIC quote_sub override - supply a sub name" ) if
202     @_ < 2
203       or
204     ! defined $_[1]
205       or
206     length ref $_[1]
207   ;
208
209   Carp::confess( "The DBIC quote_sub override expects sub name '$_[0]' to be fully qualified" )
210     unless (my $stash) = $_[0] =~ /^(.+)::/;
211
212   Carp::confess(
213     "The DBIC sub_quote override does not support 'no_install'"
214   ) if (
215     $_[3]
216       and
217     $_[3]->{no_install}
218   );
219
220   Carp::confess(
221     'The DBIC quote_sub override expects the namespace-part of sub name '
222   . "'$_[0]' to match the supplied package argument '$_[3]->{package}'"
223   ) if (
224     $_[3]
225       and
226     defined $_[3]->{package}
227       and
228     $stash ne $_[3]->{package}
229   );
230
231   my @caller = caller(0);
232   my $sq_opts = {
233     package => $caller[0],
234     hints => $caller[8],
235     warning_bits => $caller[9],
236     hintshash => $caller[10],
237     %{ $_[3] || {} },
238
239     # explicitly forced for everything
240     no_defer => 1,
241   };
242
243   weaken (
244     # just use a growing counter, no need to perform neither compaction
245     # nor any special ithread-level handling
246     $refs_closed_over_by_quote_sub_installed_crefs
247      { scalar keys %refs_closed_over_by_quote_sub_installed_crefs }
248       = $_
249   ) for grep {
250     length ref $_
251       and
252     (
253       ! DBIx::Class::_ENV_::BROKEN_WEAK_SCALARREF_VALUES
254         or
255       ref $_ ne 'SCALAR'
256     )
257   } values %{ $_[2] || {} };
258
259   Sub::Quote::quote_sub( $_[0], $_[1], $_[2]||{}, $sq_opts );
260 }
261
262 sub sigwarn_silencer ($) {
263   my $pattern = shift;
264
265   croak "Expecting a regexp" if ref $pattern ne 'Regexp';
266
267   my $orig_sig_warn = $SIG{__WARN__} || sub { CORE::warn(@_) };
268
269   return sub { &$orig_sig_warn unless $_[0] =~ $pattern };
270 }
271
272 sub perlstring ($) { q{"}. quotemeta( shift ). q{"} };
273
274 sub hrefaddr ($) { sprintf '0x%x', &refaddr||0 }
275
276 sub refdesc ($) {
277   croak "Expecting a reference" if ! length ref $_[0];
278
279   # be careful not to trigger stringification,
280   # reuse @_ as a scratch-pad
281   sprintf '%s%s(0x%x)',
282     ( defined( $_[1] = blessed $_[0]) ? "$_[1]=" : '' ),
283     reftype $_[0],
284     refaddr($_[0]),
285   ;
286 }
287
288 sub refcount ($) {
289   croak "Expecting a reference" if ! length ref $_[0];
290
291   # No tempvars - must operate on $_[0], otherwise the pad
292   # will count as an extra ref
293   B::svref_2object($_[0])->REFCNT;
294 }
295
296 sub visit_namespaces {
297   my $args = { (ref $_[0]) ? %{$_[0]} : @_ };
298
299   my $visited_count = 1;
300
301   # A package and a namespace are subtly different things
302   $args->{package} ||= 'main';
303   $args->{package} = 'main' if $args->{package} =~ /^ :: (?: main )? $/x;
304   $args->{package} =~ s/^:://;
305
306   if ( $args->{action}->($args->{package}) ) {
307     my $ns =
308       ( ($args->{package} eq 'main') ? '' :  $args->{package} )
309         .
310       '::'
311     ;
312
313     $visited_count += visit_namespaces( %$args, package => $_ ) for
314       grep
315         # this happens sometimes on %:: traversal
316         { $_ ne '::main' }
317         map
318           { $_ =~ /^(.+?)::$/ ? "$ns$1" : () }
319           do { no strict 'refs'; keys %$ns }
320     ;
321   }
322
323   $visited_count;
324 }
325
326 # FIXME In another life switch these to a polyfill like the ones in namespace::clean
327 sub get_subname ($) {
328   my $gv = B::svref_2object( $_[0] )->GV;
329   wantarray
330     ? ( $gv->STASH->NAME, $gv->NAME )
331     : ( join '::', $gv->STASH->NAME, $gv->NAME )
332   ;
333 }
334 sub set_subname ($$) {
335
336   # fully qualify name
337   splice @_, 0, 1, caller(0) . "::$_[0]"
338     if $_[0] !~ /::|'/;
339
340   &Sub::Name::subname;
341 }
342
343 sub serialize ($) {
344   local $Storable::canonical = 1;
345   nfreeze($_[0]);
346 }
347
348 sub uniq {
349   my( %seen, $seen_undef, $numeric_preserving_copy );
350   grep { not (
351     defined $_
352       ? $seen{ $numeric_preserving_copy = $_ }++
353       : $seen_undef++
354   ) } @_;
355 }
356
357 my $dd_obj;
358 sub dump_value ($) {
359   local $Data::Dumper::Indent = 1
360     unless defined $Data::Dumper::Indent;
361
362   my $dump_str = (
363     $dd_obj
364       ||=
365     do {
366       require Data::Dumper;
367       my $d = Data::Dumper->new([])
368         ->Purity(0)
369         ->Pad('')
370         ->Useqq(1)
371         ->Terse(1)
372         ->Freezer('')
373         ->Quotekeys(0)
374         ->Bless('bless')
375         ->Pair(' => ')
376         ->Sortkeys(1)
377         ->Deparse(1)
378       ;
379
380       $d->Sparseseen(1) if modver_gt_or_eq (
381         'Data::Dumper', '2.136'
382       );
383
384       $d;
385     }
386   )->Values([$_[0]])->Dump;
387
388   $dd_obj->Reset->Values([]);
389
390   $dump_str;
391 }
392
393 my $seen_loud_screams;
394 sub emit_loud_diag {
395   my $args = { ref $_[0] eq 'HASH' ? %{$_[0]} : @_ };
396
397   unless ( defined $args->{msg} and length $args->{msg} ) {
398     emit_loud_diag(
399       msg => "No 'msg' value supplied to emit_loud_diag()"
400     );
401     exit 70;
402   }
403
404   my $msg = "\n$0: $args->{msg}";
405
406   # when we die - we usually want to keep doing it
407   $args->{emit_dups} = !!$args->{confess}
408     unless exists $args->{emit_dups};
409
410   local $Carp::CarpLevel =
411     ( $args->{skip_frames} || 0 )
412       +
413     $Carp::CarpLevel
414       +
415     # hide our own frame
416     1
417   ;
418
419   my $longmess = Carp::longmess();
420
421   # different object references will thwart deduplication without this
422   ( my $key = "${msg}\n${longmess}" ) =~ s/\b0x[0-9a-f]+\b/0x.../gi;
423
424   return $seen_loud_screams->{$key} if
425     $seen_loud_screams->{$key}++
426       and
427     ! $args->{emit_dups}
428   ;
429
430   $msg .= $longmess
431     unless $msg =~ /\n\z/;
432
433   print STDERR "$msg\n"
434     or
435   print STDOUT "\n!!!STDERR ISN'T WRITABLE!!!:$msg\n";
436
437   return $seen_loud_screams->{$key}
438     unless $args->{confess};
439
440   # increment *again*, because... Carp.
441   $Carp::CarpLevel++;
442
443   # not $msg - Carp will reapply the longmess on its own
444   Carp::confess($args->{msg});
445 }
446
447
448 ###
449 ### This is *NOT* boolean.pm - deliberately not using a singleton
450 ###
451 {
452   package # hide from pause
453     DBIx::Class::_Util::_Bool;
454   use overload
455     bool => sub { ${$_[0]} },
456     fallback => 1,
457   ;
458 }
459 sub true () { my $x = 1; bless \$x, "DBIx::Class::_Util::_Bool" }
460 sub false () { my $x = 0; bless \$x, "DBIx::Class::_Util::_Bool" }
461
462 sub scope_guard (&) {
463   croak 'Calling scope_guard() in void context makes no sense'
464     if ! defined wantarray;
465
466   # no direct blessing of coderefs - DESTROY is buggy on those
467   bless [ $_[0] ], 'DBIx::Class::_Util::ScopeGuard';
468 }
469 {
470   package #
471     DBIx::Class::_Util::ScopeGuard;
472
473   sub DESTROY {
474     &DBIx::Class::_Util::detected_reinvoked_destructor;
475
476     local $@ if DBIx::Class::_ENV_::UNSTABLE_DOLLARAT;
477
478     eval {
479       $_[0]->[0]->();
480       1;
481     }
482       or
483     DBIx::Class::_Util::emit_loud_diag(
484       emit_dups => 1,
485       msg => "Execution of scope guard $_[0] resulted in the non-trappable exception:\n\n$@\n "
486     );
487   }
488 }
489
490
491 sub is_exception ($) {
492   my $e = $_[0];
493
494   # FIXME
495   # this is not strictly correct - an eval setting $@ to undef
496   # is *not* the same as an eval setting $@ to ''
497   # but for the sake of simplicity assume the following for
498   # the time being
499   return 0 unless defined $e;
500
501   my ($not_blank, $suberror);
502   {
503     local $SIG{__DIE__} if $SIG{__DIE__};
504     local $@;
505     eval {
506       # The ne() here is deliberate - a plain length($e), or worse "$e" ne
507       # will entirely obviate the need for the encolsing eval{}, as the
508       # condition we guard against is a missing fallback overload
509       $not_blank = ( $e ne '' );
510       1;
511     } or $suberror = $@;
512   }
513
514   if (defined $suberror) {
515     if (length (my $class = blessed($e) )) {
516       carp_unique( sprintf(
517         'External exception class %s implements partial (broken) overloading '
518       . 'preventing its instances from being used in simple ($x eq $y) '
519       . 'comparisons. Given Perl\'s "globally cooperative" exception '
520       . 'handling this type of brokenness is extremely dangerous on '
521       . 'exception objects, as it may (and often does) result in silent '
522       . '"exception substitution". DBIx::Class tries to work around this '
523       . 'as much as possible, but other parts of your software stack may '
524       . 'not be even aware of this. Please submit a bugreport against the '
525       . 'distribution containing %s and in the meantime apply a fix similar '
526       . 'to the one shown at %s, in order to ensure your exception handling '
527       . 'is saner application-wide. What follows is the actual error text '
528       . "as generated by Perl itself:\n\n%s\n ",
529         $class,
530         $class,
531         'http://v.gd/DBIC_overload_tempfix/',
532         $suberror,
533       ));
534
535       # workaround, keeps spice flowing
536       $not_blank = !!( length $e );
537     }
538     else {
539       # not blessed yet failed the 'ne'... this makes 0 sense...
540       # just throw further
541       die $suberror
542     }
543   }
544   elsif (
545     # a ref evaluating to '' is definitively a "null object"
546     ( not $not_blank )
547       and
548     length( my $class = ref $e )
549   ) {
550     carp_unique(
551       "Objects of external exception class '$class' stringify to '' (the "
552     . 'empty string), implementing the so called null-object-pattern. '
553     . 'Given Perl\'s "globally cooperative" exception handling using this '
554     . 'class of exceptions is extremely dangerous, as it may (and often '
555     . 'does) result in silent discarding of errors. DBIx::Class tries to '
556     . 'work around this as much as possible, but other parts of your '
557     . 'software stack may not be even aware of the problem. Please submit '
558     . "a bugreport against the distribution containing '$class'",
559     );
560
561     $not_blank = 1;
562   }
563
564   return $not_blank;
565 }
566
567 {
568   my $callstack_state;
569
570   # Recreate the logic of try(), while reusing the catch()/finally() as-is
571   #
572   # FIXME: We need to move away from Try::Tiny entirely (way too heavy and
573   # yes, shows up ON TOP of profiles) but this is a batle for another maint
574   sub dbic_internal_try (&;@) {
575
576     my $try_cref = shift;
577     my $catch_cref = undef;  # apparently this is a thing... https://rt.perl.org/Public/Bug/Display.html?id=119311
578
579     for my $arg (@_) {
580
581       if( ref($arg) eq 'Try::Tiny::Catch' ) {
582
583         croak 'dbic_internal_try() may not be followed by multiple catch() blocks'
584           if $catch_cref;
585
586         $catch_cref = $$arg;
587       }
588       elsif ( ref($arg) eq 'Try::Tiny::Finally' ) {
589         croak 'dbic_internal_try() does not support finally{}';
590       }
591       else {
592         croak(
593           'dbic_internal_try() encountered an unexpected argument '
594         . "'@{[ defined $arg ? $arg : 'UNDEF' ]}' - perhaps "
595         . 'a missing semi-colon before or ' # trailing space important
596         );
597       }
598     }
599
600     my $wantarray = wantarray;
601     my $preexisting_exception = $@;
602
603     my @ret;
604     my $all_good = eval {
605       $@ = $preexisting_exception;
606
607       local $callstack_state->{in_internal_try} = 1
608         unless $callstack_state->{in_internal_try};
609
610       # always unset - someone may have snuck it in
611       local $SIG{__DIE__} if $SIG{__DIE__};
612
613       if( $wantarray ) {
614         @ret = $try_cref->();
615       }
616       elsif( defined $wantarray ) {
617         $ret[0] = $try_cref->();
618       }
619       else {
620         $try_cref->();
621       }
622
623       1;
624     };
625
626     my $exception = $@;
627     $@ = $preexisting_exception;
628
629     if ( $all_good ) {
630       return $wantarray ? @ret : $ret[0]
631     }
632     elsif ( $catch_cref ) {
633       for ( $exception ) {
634         return $catch_cref->($exception);
635       }
636     }
637
638     return;
639   }
640
641   sub in_internal_try { !! $callstack_state->{in_internal_try} }
642 }
643
644 {
645   my $destruction_registry = {};
646
647   sub DBIx::Class::__Util_iThreads_handler__::CLONE {
648     %$destruction_registry = map {
649       (defined $_)
650         ? ( refaddr($_) => $_ )
651         : ()
652     } values %$destruction_registry;
653
654     weaken($_) for values %$destruction_registry;
655
656     # Dummy NEXTSTATE ensuring the all temporaries on the stack are garbage
657     # collected before leaving this scope. Depending on the code above, this
658     # may very well be just a preventive measure guarding future modifications
659     undef;
660   }
661
662   # This is almost invariably invoked from within DESTROY
663   # throwing exceptions won't work
664   sub detected_reinvoked_destructor {
665
666     # quick "garbage collection" pass - prevents the registry
667     # from slowly growing with a bunch of undef-valued keys
668     defined $destruction_registry->{$_} or delete $destruction_registry->{$_}
669       for keys %$destruction_registry;
670
671     if (! length ref $_[0]) {
672       emit_loud_diag(
673         emit_dups => 1,
674         msg => (caller(0))[3] . '() expects a blessed reference'
675       );
676       return undef; # don't know wtf to do
677     }
678     elsif (! defined $destruction_registry->{ my $addr = refaddr($_[0]) } ) {
679       weaken( $destruction_registry->{$addr} = $_[0] );
680       return 0;
681     }
682     else {
683       emit_loud_diag( msg => sprintf (
684         'Preventing *MULTIPLE* DESTROY() invocations on %s - an *EXTREMELY '
685       . 'DANGEROUS* condition which is *ALMOST CERTAINLY GLOBAL* within your '
686       . 'application, affecting *ALL* classes without active protection against '
687       . 'this. Diagnose and fix the root cause ASAP!!!%s',
688       refdesc $_[0],
689         ( ( $INC{'Devel/StackTrace.pm'} and ! do { local $@; eval { Devel::StackTrace->VERSION(2) } } )
690           ? " (likely culprit Devel::StackTrace\@@{[ Devel::StackTrace->VERSION ]} found in %INC, http://is.gd/D_ST_refcap)"
691           : ''
692         )
693       ));
694
695       return 1;
696     }
697   }
698 }
699
700 my $module_name_rx = qr/ \A [A-Z_a-z] [0-9A-Z_a-z]* (?: :: [0-9A-Z_a-z]+ )* \z /x;
701 my $ver_rx =         qr/ \A [0-9]+ (?: \. [0-9]+ )* (?: \_ [0-9]+ )*        \z /x;
702
703 sub modver_gt_or_eq ($$) {
704   my ($mod, $ver) = @_;
705
706   croak "Nonsensical module name supplied"
707     if ! defined $mod or $mod !~ $module_name_rx;
708
709   croak "Nonsensical minimum version supplied"
710     if ! defined $ver or $ver !~ $ver_rx;
711
712   no strict 'refs';
713   my $ver_cache = ${"${mod}::__DBIC_MODULE_VERSION_CHECKS__"} ||= ( $mod->VERSION
714     ? {}
715     : croak "$mod does not seem to provide a version (perhaps it never loaded)"
716   );
717
718   ! defined $ver_cache->{$ver}
719     and
720   $ver_cache->{$ver} = do {
721
722     local $SIG{__WARN__} = sigwarn_silencer( qr/\Qisn't numeric in subroutine entry/ )
723       if SPURIOUS_VERSION_CHECK_WARNINGS;
724
725     local $SIG{__DIE__} if $SIG{__DIE__};
726     local $@;
727     eval { $mod->VERSION($ver) } ? 1 : 0;
728   };
729
730   $ver_cache->{$ver};
731 }
732
733 sub modver_gt_or_eq_and_lt ($$$) {
734   my ($mod, $v_ge, $v_lt) = @_;
735
736   croak "Nonsensical maximum version supplied"
737     if ! defined $v_lt or $v_lt !~ $ver_rx;
738
739   return (
740     modver_gt_or_eq($mod, $v_ge)
741       and
742     ! modver_gt_or_eq($mod, $v_lt)
743   ) ? 1 : 0;
744 }
745
746 {
747
748   sub describe_class_methods {
749     my $args = (
750       ref $_[0] eq 'HASH'                 ? $_[0]
751     : ( @_ == 1 and ! length ref $_[0] )  ? { class => $_[0] }
752     :                                       { @_ }
753     );
754
755     my ($class, $requested_mro) = @{$args}{qw( class use_mro )};
756
757     croak "Expecting a class name either as the sole argument or a 'class' option"
758       if not defined $class or $class !~ $module_name_rx;
759
760     $requested_mro ||= mro::get_mro($class);
761
762     # mro::set_mro() does not bump pkg_gen - WHAT THE FUCK?!
763     my $query_cache_key = "$class|$requested_mro";
764
765     my $internal_cache_key =
766       ( mro::get_mro($class) eq $requested_mro )
767         ? $class
768         : $query_cache_key
769     ;
770
771     # use a cache on old MRO, since while we are recursing in this function
772     # nothing can possibly change (the speedup is immense)
773     # (yes, people could be tie()ing the stash and adding methods on access
774     # but there is a limit to how much crazy can be supported here)
775     #
776     # we use the cache for linear_isa lookups on new MRO as well - it adds
777     # a *tiny* speedup, and simplifies the code a lot
778     #
779     local $__describe_class_query_cache->{'!internal!'} = {}
780       unless $__describe_class_query_cache->{'!internal!'};
781
782     my $my_gen = 0;
783
784     $my_gen += get_real_pkg_gen($_) for ( my @full_ISA = (
785
786       @{
787         $__describe_class_query_cache->{'!internal!'}{$internal_cache_key}{linear_isa}
788           ||=
789         mro::get_linear_isa($class, $requested_mro)
790       },
791
792       ((
793         $__describe_class_query_cache->{'!internal!'}{$class}{is_universal}
794           ||=
795         mro::is_universal($class)
796       ) ? () : @{
797         $__describe_class_query_cache->{'!internal!'}{UNIVERSAL}{linear_isa}
798           ||=
799         mro::get_linear_isa("UNIVERSAL")
800       }),
801
802     ));
803
804     my $slot = $__describe_class_query_cache->{$query_cache_key} ||= {};
805
806     unless ( ($slot->{cumulative_gen}||0) == $my_gen ) {
807
808       # reset
809       %$slot = (
810         class => $class,
811         isa => { map { $_ => 1 } @full_ISA },
812         linear_isa => [
813           @{ $__describe_class_query_cache->{'!internal!'}{$internal_cache_key}{linear_isa} }
814             [ 1 .. $#{$__describe_class_query_cache->{'!internal!'}{$internal_cache_key}{linear_isa}} ]
815         ],
816         mro => {
817           type => $requested_mro,
818           is_c3 => ( ($requested_mro eq 'c3') ? 1 : 0 ),
819         },
820         cumulative_gen => $my_gen,
821       );
822
823       # remove ourselves from ISA
824       shift @full_ISA;
825
826       # ensure the cache is populated for the parents, code below can then
827       # efficiently operate over the query_cache directly
828       describe_class_methods($_) for reverse @full_ISA;
829
830       no strict 'refs';
831
832       # combine full ISA-order inherited and local method list into a
833       # "shadowing stack"
834
835       (
836         unshift @{ $slot->{methods}{$_->{name}} }, $_
837
838           and
839
840         (
841           $_->{via_class} ne $class
842             or
843           $slot->{methods_defined_in_class}{$_->{name}} = $_
844         )
845
846           and
847
848         @{ $slot->{methods}{$_->{name}} } > 1
849
850           and
851
852         $slot->{methods_with_supers}{$_->{name}} = $slot->{methods}{$_->{name}}
853
854       ) for (
855
856         # what describe_class_methods for @full_ISA produced above
857         ( map { values %{
858           $__describe_class_query_cache->{$_}{methods_defined_in_class} || {}
859         } } map { "$_|" . mro::get_mro($_) } reverse @full_ISA ),
860
861         # our own non-cleaned subs + their attributes
862         ( map {
863           (
864             # need to account for dummy helper crefs under OLD_MRO
865             (
866               ! DBIx::Class::_ENV_::OLD_MRO
867                 or
868               ! ( ( $Class::C3::MRO{$class} || {} )->{methods}{$_} )
869             )
870               and
871             # these 2 OR-ed checks are sufficient for 5.10+
872             (
873               ref(\ "${class}::"->{$_} ) ne 'GLOB'
874                 or
875               defined( *{ "${class}::"->{$_} }{CODE} )
876             )
877           ) ? {
878               via_class => $class,
879               name => $_,
880               attributes => { map { $_ => 1 } do {
881                 my @attrs;
882                 local $@;
883                 local $SIG{__DIE__} if $SIG{__DIE__};
884                 # attributes::get may throw on blessed-false crefs :/
885                 eval { @attrs = attributes::get( \&{"${class}::${_}"} ); 1 }
886                   or warn "Unable to determine attributes of the \\&${class}::$_ method due to following error: $@";
887                 @attrs;
888               } },
889             }
890             : ()
891         } keys %{"${class}::"} )
892       );
893
894
895       # recalculate the pkg_gen on newer perls under Taint mode,
896       # because of shit like:
897       # perl -T -Mmro -e 'package Foo; sub bar {}; defined( *{ "Foo::"->{bar}}{CODE} ) and warn mro::get_pkg_gen("Foo") for (1,2,3)'
898       #
899       if (
900         ! DBIx::Class::_ENV_::OLD_MRO
901           and
902         ${^TAINT}
903       ) {
904
905         $slot->{cumulative_gen} = 0;
906         $slot->{cumulative_gen} += get_real_pkg_gen($_)
907           for $class, @full_ISA;
908       }
909     }
910
911     # RV
912     +{ %$slot };
913   }
914 }
915
916
917 #
918 # Why not just use some higher-level module or at least File::Spec here?
919 # Because:
920 # 1)  This is a *very* rarely used function, and the deptree is large
921 #     enough already as it is
922 #
923 # 2)  (more importantly) Our tooling is utter shit in this area. There
924 #     is no comprehensive support for UNC paths in PathTools and there
925 #     are also various small bugs in representation across different
926 #     path-manipulation CPAN offerings.
927 #
928 # Since this routine is strictly used for logical path processing (it
929 # *must* be able to work with not-yet-existing paths), use this seemingly
930 # simple but I *think* complete implementation to feed to other consumers
931 #
932 # If bugs are ever uncovered in this routine, *YOU ARE URGED TO RESIST*
933 # the impulse to bring in an external dependency. During runtime there
934 # is exactly one spot that could potentially maybe once in a blue moon
935 # use this function. Keep it lean.
936 #
937 sub parent_dir ($) {
938   ( $_[0] =~ m{  [\/\\]  ( \.{0,2} ) ( [\/\\]* ) \z }x )
939     ? (
940       $_[0]
941         .
942       ( ( length($1) and ! length($2) ) ? '/' : '' )
943         .
944       '../'
945     )
946     : (
947       require File::Spec
948         and
949       File::Spec->catpath (
950         ( File::Spec->splitpath( "$_[0]" ) )[0,1],
951         '/',
952       )
953     )
954   ;
955 }
956
957 sub mkdir_p ($) {
958   require File::Path;
959   # do not ask for a recent version, use 1.x API calls
960   File::Path::mkpath([ "$_[0]" ]);  # File::Path does not like objects
961 }
962
963
964 {
965   my $list_ctx_ok_stack_marker;
966
967   sub fail_on_internal_wantarray () {
968     return if $list_ctx_ok_stack_marker;
969
970     if (! defined wantarray) {
971       croak('fail_on_internal_wantarray() needs a tempvar to save the stack marker guard');
972     }
973
974     my $cf = 1;
975     while ( ( (CORE::caller($cf+1))[3] || '' ) =~ / :: (?:
976
977       # these are public API parts that alter behavior on wantarray
978       search | search_related | slice | search_literal
979
980         |
981
982       # these are explicitly prefixed, since we only recognize them as valid
983       # escapes when they come from the guts of CDBICompat
984       CDBICompat .*? :: (?: search_where | retrieve_from_sql | retrieve_all )
985
986     ) $/x ) {
987       $cf++;
988     }
989
990     my ($fr, $want, $argdesc);
991     {
992       package DB;
993       $fr = [ CORE::caller($cf) ];
994       $want = ( CORE::caller($cf-1) )[5];
995       $argdesc = ref $DB::args[0]
996         ? DBIx::Class::_Util::refdesc($DB::args[0])
997         : 'non '
998       ;
999     };
1000
1001     if (
1002       $want and $fr->[0] =~ /^(?:DBIx::Class|DBICx::)/
1003     ) {
1004       DBIx::Class::Exception->throw( sprintf (
1005         "Improper use of %s instance in list context at %s line %d\n\n    Stacktrace starts",
1006         $argdesc, @{$fr}[1,2]
1007       ), 'with_stacktrace');
1008     }
1009
1010     weaken( $list_ctx_ok_stack_marker = my $mark = [] );
1011
1012     $mark;
1013   }
1014 }
1015
1016 sub fail_on_internal_call {
1017   my ($fr, $argdesc);
1018   {
1019     package DB;
1020     $fr = [ CORE::caller(1) ];
1021     $argdesc = ref $DB::args[0]
1022       ? DBIx::Class::_Util::refdesc($DB::args[0])
1023       : ( $DB::args[0] . '' )
1024     ;
1025   };
1026
1027   my @fr2;
1028   # need to make allowance for a proxy-yet-direct call
1029   my $check_fr = (
1030     $fr->[0] eq 'DBIx::Class::ResultSourceProxy'
1031       and
1032     @fr2 = (CORE::caller(2))
1033       and
1034     (
1035       ( $fr->[3] =~ /([^:])+$/ )[0]
1036         eq
1037       ( $fr2[3] =~ /([^:])+$/ )[0]
1038     )
1039   )
1040     ? \@fr2
1041     : $fr
1042   ;
1043
1044   if (
1045     $argdesc
1046       and
1047     $check_fr->[0] =~ /^(?:DBIx::Class|DBICx::)/
1048       and
1049     $check_fr->[1] !~ /\b(?:CDBICompat|ResultSetProxy)\b/  # no point touching there
1050   ) {
1051     DBIx::Class::Exception->throw( sprintf (
1052       "Illegal internal call of indirect proxy-method %s() with argument '%s': examine the last lines of the proxy method deparse below to determine what to call directly instead at %s on line %d\n\n%s\n\n    Stacktrace starts",
1053       $fr->[3], $argdesc, @{$fr}[1,2], ( $fr->[6] || do {
1054         require B::Deparse;
1055         no strict 'refs';
1056         B::Deparse->new->coderef2text(\&{$fr->[3]})
1057       }),
1058     ), 'with_stacktrace');
1059   }
1060 }
1061
1062 1;