# but of course
BROKEN_FORK => ($^O eq 'MSWin32') ? 1 : 0,
+ BROKEN_GOTO => ($] < '5.008003') ? 1 : 0,
+
HAS_ITHREADS => $Config{useithreads} ? 1 : 0,
# ::Runmode would only be loaded by DBICTest, which in turn implies t/
ASSERT_NO_INTERNAL_WANTARRAY => $ENV{DBIC_ASSERT_NO_INTERNAL_WANTARRAY} ? 1 : 0,
+ ASSERT_NO_INTERNAL_INDIRECT_CALLS => $ENV{DBIC_ASSERT_NO_INTERNAL_INDIRECT_CALLS} ? 1 : 0,
+
+ STRESSTEST_UTF8_UPGRADE_GENERATED_COLLAPSER_SOURCE => $ENV{DBIC_STRESSTEST_UTF8_UPGRADE_GENERATED_COLLAPSER_SOURCE} ? 1 : 0,
+
+ STRESSTEST_COLUMN_INFO_UNAWARE_STORAGE => $ENV{DBIC_STRESSTEST_COLUMN_INFO_UNAWARE_STORAGE} ? 1 : 0,
+
IV_SIZE => $Config{ivsize},
OS_NAME => $^O,
# Carp::Skip to the rescue soon
use DBIx::Class::Carp '^DBIx::Class|^DBICTest';
+use B ();
use Carp 'croak';
-use Scalar::Util qw(weaken blessed reftype);
+use Storable 'nfreeze';
+use Scalar::Util qw(weaken blessed reftype refaddr);
use List::Util qw(first);
-use overload ();
+use Sub::Quote qw(qsub quote_sub);
+
+# Already correctly prototyped: perlbrew exec perl -MStorable -e 'warn prototype \&Storable::dclone'
+BEGIN { *deep_clone = \&Storable::dclone }
use base 'Exporter';
our @EXPORT_OK = qw(
- sigwarn_silencer modver_gt_or_eq fail_on_internal_wantarray
- refdesc refcount hrefaddr is_exception
- is_plain_value is_literal_value
+ sigwarn_silencer modver_gt_or_eq modver_gt_or_eq_and_lt
+ fail_on_internal_wantarray fail_on_internal_call
+ refdesc refcount hrefaddr is_exception detect_reinvoked_destructor
+ quote_sub qsub perlstring serialize deep_clone
UNRESOLVABLE_CONDITION
);
return sub { &$orig_sig_warn unless $_[0] =~ $pattern };
}
-sub hrefaddr ($) { sprintf '0x%x', &Scalar::Util::refaddr||0 }
+sub perlstring ($) { q{"}. quotemeta( shift ). q{"} };
+
+sub hrefaddr ($) { sprintf '0x%x', &refaddr||0 }
sub refdesc ($) {
croak "Expecting a reference" if ! length ref $_[0];
sprintf '%s%s(0x%x)',
( defined( $_[1] = blessed $_[0]) ? "$_[1]=" : '' ),
reftype $_[0],
- Scalar::Util::refaddr($_[0]),
+ refaddr($_[0]),
;
}
sub refcount ($) {
croak "Expecting a reference" if ! length ref $_[0];
- require B;
# No tempvars - must operate on $_[0], otherwise the pad
# will count as an extra ref
B::svref_2object($_[0])->REFCNT;
}
+sub serialize ($) {
+ local $Storable::canonical = 1;
+ nfreeze($_[0]);
+}
+
sub is_exception ($) {
my $e = $_[0];
if (defined $suberror) {
if (length (my $class = blessed($e) )) {
carp_unique( sprintf(
- 'External exception object %s implements partial (broken) '
- . 'overloading preventing it from being used in simple ($x eq $y) '
+ 'External exception class %s implements partial (broken) overloading '
+ . 'preventing its instances from being used in simple ($x eq $y) '
. 'comparisons. Given Perl\'s "globally cooperative" exception '
. 'handling this type of brokenness is extremely dangerous on '
. 'exception objects, as it may (and often does) result in silent '
. 'to the one shown at %s, in order to ensure your exception handling '
. 'is saner application-wide. What follows is the actual error text '
. "as generated by Perl itself:\n\n%s\n ",
- refdesc $e,
+ $class,
$class,
'http://v.gd/DBIC_overload_tempfix/',
$suberror,
return $not_blank;
}
+{
+ my $destruction_registry = {};
+
+ sub CLONE {
+ $destruction_registry = { map
+ { defined $_ ? ( refaddr($_) => $_ ) : () }
+ values %$destruction_registry
+ };
+ }
+
+ # This is almost invariably invoked from within DESTROY
+ # throwing exceptions won't work
+ sub detect_reinvoked_destructor {
+
+ # quick "garbage collection" pass - prevents the registry
+ # from slowly growing with a bunch of undef-valued keys
+ defined $destruction_registry->{$_} or delete $destruction_registry->{$_}
+ for keys %$destruction_registry;
+
+ unless (length ref $_[0]) {
+ printf STDERR '%s() expects a reference %s',
+ (caller(0))[3],
+ Carp::longmess,
+ ;
+ return undef; # don't know wtf to do
+ }
+
+ if (! defined $destruction_registry->{ my $addr = refaddr($_[0]) } ) {
+ weaken( $destruction_registry->{$addr} = $_[0] );
+ return 0;
+ }
+ else {
+ carp_unique ( sprintf (
+ 'Preventing *MULTIPLE* DESTROY() invocations on %s - an *EXTREMELY '
+ . 'DANGEROUS* condition which is *ALMOST CERTAINLY GLOBAL* within your '
+ . 'application, affecting *ALL* classes without active protection against '
+ . 'this. Diagnose and fix the root cause ASAP!!!%s',
+ refdesc $_[0],
+ ( ( $INC{'Devel/StackTrace.pm'} and ! do { local $@; eval { Devel::StackTrace->VERSION(2) } } )
+ ? " (likely culprit Devel::StackTrace\@@{[ Devel::StackTrace->VERSION ]} found in %INC, http://is.gd/D_ST_refcap)"
+ : ''
+ )
+ ));
+
+ return 1;
+ }
+ }
+}
+
sub modver_gt_or_eq ($$) {
my ($mod, $ver) = @_;
eval { $mod->VERSION($ver) } ? 1 : 0;
}
-sub is_literal_value ($) {
- (
- ref $_[0] eq 'SCALAR'
- or
- ( ref $_[0] eq 'REF' and ref ${$_[0]} eq 'ARRAY' )
- ) ? 1 : 0;
-}
+sub modver_gt_or_eq_and_lt ($$$) {
+ my ($mod, $v_ge, $v_lt) = @_;
-# FIXME XSify - this can be done so much more efficiently
-sub is_plain_value ($) {
- no strict 'refs';
- (
- # plain scalar
- (! length ref $_[0])
- or
- (
- blessed $_[0]
- and
- # deliberately not using Devel::OverloadInfo - the checks we are
- # intersted in are much more limited than the fullblown thing, and
- # this is a relatively hot piece of code
- (
- # FIXME - DBI needs fixing to stringify regardless of DBD
- #
- # either has stringification which DBI SHOULD prefer out of the box
- #first { *{$_ . '::(""'}{CODE} } @{ mro::get_linear_isa( ref $_[0] ) }
- overload::Method($_[0], '""')
- or
- # has nummification and fallback is *not* disabled
- (
- $_[1] = first { *{"${_}::(0+"}{CODE} } @{ mro::get_linear_isa( ref $_[0] ) }
- and
- ( ! defined ${"$_[1]::()"} or ${"$_[1]::()"} )
- )
- )
- )
+ croak "Nonsensical maximum version supplied"
+ if ! defined $v_lt or $v_lt =~ /[^0-9\.\_]/;
+
+ return (
+ modver_gt_or_eq($mod, $v_ge)
+ and
+ ! modver_gt_or_eq($mod, $v_lt)
) ? 1 : 0;
}
{
my $list_ctx_ok_stack_marker;
- sub fail_on_internal_wantarray {
+ sub fail_on_internal_wantarray () {
return if $list_ctx_ok_stack_marker;
if (! defined wantarray) {
$cf++;
}
+ my ($fr, $want, $argdesc);
+ {
+ package DB;
+ $fr = [ caller($cf) ];
+ $want = ( caller($cf-1) )[5];
+ $argdesc = ref $DB::args[0]
+ ? DBIx::Class::_Util::refdesc($DB::args[0])
+ : 'non '
+ ;
+ };
+
if (
- (caller($cf))[0] =~ /^(?:DBIx::Class|DBICx::)/
+ $want and $fr->[0] =~ /^(?:DBIx::Class|DBICx::)/
) {
DBIx::Class::Exception->throw( sprintf (
- "Improper use of %s instance in list context at %s line %d\n\n\tStacktrace starts",
- refdesc($_[0]), (caller($cf))[1,2]
+ "Improper use of %s instance in list context at %s line %d\n\n Stacktrace starts",
+ $argdesc, @{$fr}[1,2]
), 'with_stacktrace');
}
}
}
+sub fail_on_internal_call {
+ my ($fr, $argdesc);
+ {
+ package DB;
+ $fr = [ caller(1) ];
+ $argdesc = ref $DB::args[0]
+ ? DBIx::Class::_Util::refdesc($DB::args[0])
+ : undef
+ ;
+ };
+
+ if (
+ $argdesc
+ and
+ $fr->[0] =~ /^(?:DBIx::Class|DBICx::)/
+ and
+ $fr->[1] !~ /\b(?:CDBICompat|ResultSetProxy)\b/ # no point touching there
+ ) {
+ DBIx::Class::Exception->throw( sprintf (
+ "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",
+ $fr->[3], $argdesc, @{$fr}[1,2], ( $fr->[6] || do {
+ require B::Deparse;
+ no strict 'refs';
+ B::Deparse->new->coderef2text(\&{$fr->[3]})
+ }),
+ ), 'with_stacktrace');
+ }
+}
+
1;