Devel-Trepan
view release on metacpan or search on metacpan
lib/Devel/Trepan/DB/LineCache.pm view on Meta::CPAN
$fh->close();
$opts ||= {};
$opts->{use_perl_d_file} = 0;
update_cache($tempfile, $opts);
$script2file{$script} = $tempfile;
return $tempfile;
}
sub map_file_line($$)
{
my ($file, $line) = @_;
if (exists $file2file_remap_lines{$file}) {
my $triplet_ref = $file2file_remap_lines{$file};
for my $triplet (@$triplet_ref) {
my ($from_file, $range_ref, $start) = @$triplet;
my @range = @$range_ref;
if ( $range[0] >= $line && $range[-1] <= $line) {
my $from_file = $from_file || $file;
return [$from_file, $start+$line-$range[0]];
}
}
}
return ($file, $line);
}
=pod
=head2 filename_is_eval
B<filename_is_eval($filename)> => I<boolean>
Return I<true> if $filename matches one of the pseudo-filename strings
that get created for by I<eval()>.
=cut
sub filename_is_eval($)
{
my $filename = shift;
return 0 unless defined $filename;
return !!
($filename =~ /^\(eval \d+\)|-e$/
# SelfLoader does this:
|| $filename =~ /^sub \S+::\S+/
);
}
=pod
=head2 update_script_cache
B<update_script_cache($script, $opts)> => I<boolean>
Update a cache entry for an pseudo eval-string file name. If something
is wrong, return I<undef>. Return I<true> if the cache was updated and
I<false> if not.
=cut
sub update_script_cache($$)
{
my ($script, $opts) = @_;
return 0 unless filename_is_eval($script);
my $string = $opts->{string};
my $lines_href = {};
if (defined($string)) {
my @lines = split(/\n/, $string);
$lines_href->{plain} = \@lines;
} else {
if ($script eq $DB::filename) {
## SelfLoader evals
if (!@DB::line && $script =~/^sub (\S+)/) {
my $func = $1;
my $string = $Devel::Trepan::SelfLoader::Cache{$func};
return 0 unless $string;
$string =~ s/^\n#line 1.+\n//;
@DB::line = split(/\n/, $string);
}
# Should be the same as the else case,
# but just in case...
$lines_href->{plain} = \@DB::line;
$string = join("\n", @DB::line);
} else {
no strict;
$lines_href->{plain} = \@{"_<$script"};
$string = join("\n", @{"_<$script"});
}
return 0 unless length($string);
}
$lines_href->{$opts->{output}} = highlight_string($string) if
$opts->{output} && $opts->{output} ne 'plain';
my $entry = {
lines_href => $lines_href,
};
$script_cache{$script} = $entry;
return 1;
}
=head2
B<dualvar_lines($file_or_string, $is_file, $mark_trace)> =>
# I<list of dual-var strings>
# Routine to create dual numeric/string values for
# C<$file_or_string>. A list reference is returned. In string context
# it is the line with a trailing "\n". In a numeric context it is 0 or
# 1 if $mark_trace is set and B::CodeLines determines it is a trace
# line.
#
# Note: Perl implementations seem to put a COP address inside
# @DB::db_line when there are trace lines. I am not sure if this is
# specified as part of the API. We don't do that here but (and might
# even if it is not officially defined in the API.) Instead put value
# 1.
#
=cut
# FIXME: $mark_trace may be something of a hack. Without it we can
lib/Devel/Trepan/DB/LineCache.pm view on Meta::CPAN
a debugger is called via Enbugger which turn on debugging late so source
files might not have been read in.
=cut
sub load_file($;$) {
my ($filename, $eval_string) = @_;
# The symbols by which we'll know ye.
my $base_symname = "_<$filename";
my $symname = "main::$base_symname";
no strict 'refs';
# Note: dualvar_lines updates @$synmame;
if (defined($eval_string)) {
dualvar_lines($eval_string, \@$symname, 0, 1);
} else {
dualvar_lines($filename, \@$symname, 1, 1);
}
$$symname ||= $filename;
return;
}
=head2 readlines
B<readlines(I<$filename>)> => I<list of strings>
Return a a list of strings for I<$filename>. If we can't read
I<$filename> retun I<undef>. Each line will have a "\n" at the end.
=cut
sub readlines($)
{
my $path = shift;
if (-r $path) {
my $fh;
open($fh, '<', $path);
seek $fh, 0, 0;
my @lines = <$fh>;
close $fh;
return @lines;
} else {
return undef;
}
}
=head2 update_cache
B<update_cache($filename, [, $opts]>
Update a cache entry. If something's wrong, return I<undef>. Return
the expanded file name if the cache was updated and I<false> if not. If
$I<$opts-E<gt>{use_perl_d_file}> is I<true>, use that as the source for the
lines of the file.
=cut
sub update_cache($;$)
{
my ($filename, $opts) = @_;
my $read_file = 0;
$opts = {} unless defined $opts;
my $use_perl_d_file = $opts->{use_perl_d_file};
$use_perl_d_file = 1 unless defined $use_perl_d_file;
return undef unless $filename;
delete $file_cache{$filename};
my $is_eval = filename_is_eval($filename);
my $path = $filename;
unless ($is_eval) {
$path = abs_path($filename) if -f $filename;
}
my $lines_href;
my $trace_nums = {};
my $stat;
if ($use_perl_d_file) {
my @list = ($filename);
if ($is_eval) {
cache_script($filename);
## FIXME: create a temporary file in script2file;
}
push @list, $file2file_remap{$path} if exists $file2file_remap{$path};
for my $name (@list) {
no strict; # Avoid string as ARRAY ref error message
if (scalar @{"main::_<$name"}) {
$stat = File::stat::stat($path);
}
my $raw_lines = \@{"main::_<$name"};
# Perl sometimes doesn't seem to save all file data, such
# as those intended for POD or possibly those after
# __END__. But we want these, so we'll have to read the
# file the old-fashioned way and check lines. Variable
# $incomplete records if there was a mismatch.
my $incomplete = 0;
if (-r $path) {
my @lines_check = readlines($path);
my @lines = @$raw_lines;
my $totally_empty = 1;
for (my $i=1; $i<=$#lines; $i++) {
if (defined $raw_lines->[$i]) {
$totally_empty = 0;
last;
}
}
if ($totally_empty) {
load_file($filename);
$trace_nums = $file_cache{$filename}{trace_nums};
} else {
for (my $i=1; $i<=$#lines; $i++) {
if (defined $raw_lines->[$i]) {
no warnings;
$trace_nums->{$i} = (-$raw_lines->[$i]) if
(+$raw_lines->[$i]) != 0;
$incomplete = 1 if $raw_lines->[$i] ne $lines[$i];
} else {
( run in 0.372 second using v1.01-cache-2.11-cpan-d7f47b0818f )