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