Devel-Leak-Cb

 view release on metacpan or  search on metacpan

lib/Devel/Leak/Cb.pm  view on Meta::CPAN


BEGIN {
	if ($ENV{DEBUG_CB}) {
		my $debug = $ENV{DEBUG_CB};
		*DEBUG = sub () { $debug };
	} else {
		*DEBUG = sub () { 0 };
	}
}


BEGIN {
	if (DEBUG){
		eval { require Sub::Identify;   Sub::Identify->import('sub_fullname'); 1 } or *sub_fullname = sub { return };
		eval { require Sub::Name;       Sub::Name->import('subname');          1 } or *subname      = sub { $_[1] };
		eval { require Devel::Refcount; Devel::Refcount->import('refcount');   1 } or *refcount     = sub { -1 };
		*COUNT = sub () {
			for (keys %DEF) {
				my $d = delete $DEF{$_};
				#print STDERR "Counting $_ [ @$d ]";
				$d->[0] or next;
				my $name = $d->[4] ? $d->[1].'::cb.'.$d->[4] : sub_fullname($d->[0]) || $d->[1].'::cb.__ANON__';
				substr($name,-10) eq '::__ANON__' and substr($name,-10) = '::cb.__ANON__';
				warn "Leaked: $name (refs:".refcount($d->[0]).") defined at $d->[2] line $d->[3]\n".(DEBUG > 1 ? findref($d->[0]) : '' );
			}
		};
	} else {
		*COUNT = sub () {};
	}
	if (DEBUG>1) {
		eval { require Devel::FindRef;  *findref = \&Devel::FindRef::track; 1 } or *findref  = sub { "No Devel::FindRef installed\n" };
	}
}

sub import{
	my $class = shift;
	my $caller = caller;
	Devel::Declare->setup_for(
		$caller,
		{ 'cb' => { const => \&parse } }
	);
	{
		no strict 'refs';
		*{$caller.'::cb' } = sub() { 1 };
	}
}

sub __cb__::DESTROY {
	#print STDERR "destroy $_[0]\n";
	delete($DEF{int $_[0]});
};

our $LASTNAME;

sub remebmer($) {
	$LASTNAME = $_[0];
	return 1;
}

sub wrapper (&) {
	$DEF{int $_[0]} = [ $_[0], (caller)[0..2], $LASTNAME ];
	weaken($DEF{int $_[0]}[0]);
	subname($DEF{int $_[0]}[1].'::cb.'.$LASTNAME => $_[0]) if $LASTNAME;
	$LASTNAME = undef;
	return bless $_[0],'__cb__';
}

sub parse {
	my $offset = $_[1];
	$offset += Devel::Declare::toke_move_past_token($offset);
	$offset += Devel::Declare::toke_skipspace($offset);
	my $name = 'undef';
	my $line = Devel::Declare::get_linestr();
	
	if (
		substr($line,$offset,1) =~ /^('|")/ # '
		and my $len = Devel::Declare::toke_scan_str($offset)
	){
		my $lex = $1;
		my $st = Devel::Declare::get_lex_stuff();
		Devel::Declare::clear_lex_stuff();
		#warn "Got lex $lex >$st<";
		my $linestr = Devel::Declare::get_linestr();
		if ( $len < 0 or $offset + $len > length($linestr) ) {
			require Carp;
			Carp::croak("Unbalanced text supplied");
		}
		substr($linestr, $offset, $len) = '';
		Devel::Declare::set_linestr($linestr);
		$name = qq{$lex$st$lex};
		
	}
	elsif (my $len = Devel::Declare::toke_scan_word($offset, 1)) {
		my $linestr = Devel::Declare::get_linestr();
		$name = substr($linestr, $offset, $len);
		substr($linestr, $offset, $len) = '';
		Devel::Declare::set_linestr($linestr);
		$offset += Devel::Declare::toke_skipspace($offset);
		$name = qq{'$name'};
	}
	elsif (substr(my $line = Devel::Declare::get_linestr(),$offset,1) eq '$') {
		if (my $len = Devel::Declare::toke_scan_word($offset+1, 1)) {
			my $linestr = Devel::Declare::get_linestr();
			$name = substr($linestr, $offset, $len+1);
			substr($linestr, $offset, $len+1) = '';
			Devel::Declare::set_linestr($linestr);
			$offset += Devel::Declare::toke_skipspace($offset);
			$name = qq{$name};
		} else {
			die("Bad syntax: $line at @{[ (caller 1)[1] ]}");
		}
	}
	
	my $linestr = Devel::Declare::get_linestr();
	if (DEBUG) {
		substr($linestr,$offset,0) = '&& Devel::Leak::Cb::remebmer('.$name.') && Devel::Leak::Cb::wrapper ';
		Devel::Declare::set_linestr($linestr);
	} else {
		substr($linestr,$offset,0) = '&& sub ';
		Devel::Declare::set_linestr($linestr);
	}



( run in 1.688 second using v1.01-cache-2.11-cpan-54e63673c56 )