bschmer has asked for the wisdom of the Perl Monks concerning the following question:

So I've been playing around with trying to get to the real code for a subroutine as determined by the subroutine ref. While I know that this is not always obtainable, I would like to try to track it down nonetheless. I've looked at B::Deparse and that I don't believe it to be quite what I'm looking for as I would like to see comments and the like in the code. Module::Info->subroutines will give me a pointer to the file (which I can also get from %INC) along with the line number of the first and last op in the sub, but those don't quite get me what I want either since the beginning and the end of the routine is chopped off. While finding the beginning of the routine in this case is pretty easy, finding the end is more difficult.

Does anyone out there in Monkland have any tips on how to do this? I've included my very-much-in-progress code below.

#!/usr/bin/perl use Module::Info; use IO::File; use Data::Dumper; my %modinfos; sub _showcode { my ($pkg, $sub) = @_; my $fh; if (!exists($modinfos{$pkg}{parsedfile})){ if ($fh = new IO::File($modinfos{$pkg}{mod}->file())){ my $count = 0; do { my $offset = $fh->tell(); $modinfos{$pkg}{parsedfile}[$count++] = $offset; } while ($fh->getline); } else { die("Could not open $filename"); } } else { $fh = new IO::File($modinfos{$pkg}{mod}->file()); } if ($fh){ my $s = $modinfos{$pkg}{subs}->{$sub}->{start}; my $f = $modinfos{$pkg}{subs}->{$sub}->{end}; my $sp = $modinfos{$pkg}{parsedfile}[$s]; my $fp = $modinfos{$pkg}{parsedfile}[$f]; my $delta = $fp - $sp; if ($delta <= 0){ warn("Error retrieving code for $varName, start $s->$sp, end $ +f->$fp, delta = $delta, $filename"); $s = $f - 1; $sp = $modinfos{$pkg}{parsedfile}[$s]; $fp = $modinfos{$pkg}{parsedfile}[$f]; $delta = $fp - $sp; } print("$s($sp) to $f($fp) delta = $delta for $sub\n"); $fh->seek($sp, 0); my $cbuf; my $r = $fh->read($cbuf, $delta); # Need to back up to beginning of sub from the "start" while ($cbuf !~ /sub\s+$sub/){ $s--; last if ($s < 0); $sp = $modinfos{$pkg}{parsedfile}[$s]; $fp = $modinfos{$pkg}{parsedfile}[$s+1]; my $delta = $fp - $sp; $fh->seek($sp, 0); my $cbuf2; my $r = $fh->read($cbuf2, $delta); $cbuf = $cbuf2 . $cbuf; } $s = $f-1; my @used = $modinfos{$pkg}{mod}->modules_used(); my $usedstr = ""; foreach my $used (@used){ $usedstr .= "use $used;\n"; } while (1){ print("Sending to eval => $usedstr$cbuf\n"); my $ref = eval "$usedstr$cbuf"; if ($@){ # warn($@); } else { last; } $s++; last if ($s > $modinfos{$pkg}{parsedfile}); $sp = $modinfos{$pkg}{parsedfile}[$s]; $fp = $modinfos{$pkg}{parsedfile}[$s+1]; my $delta = $fp - $sp; $fh->seek($sp, 0); my $cbuf2; my $r = $fh->read($cbuf2, $delta); $cbuf .= $cbuf2; } print("Code is => \n$cbuf\n"); } } sub UNIVERSAL::Introspect { my @recurse = (@_); while (@recurse){ my $cur = shift @recurse; my $pkg; if ($pkg = ref($cur)){ print("$cur is a $pkg\n"); } else { $pkg = $cur; $cur = $pkg->new(); } if (!exists($modinfos{$pkg}{mod})){ my @mods = Module::Info->all_installed($pkg); if (@mods != 1){ warn("Did not get proper number of modules: " . Dumper(\@mods) +); return; } $modinfos{$pkg}{mod} = shift @mods; } if (grep /^$pkg$/, @done){ print "Skipping $pkg\n"; next; } my %subs = $modinfos{$pkg}{mod}->subroutines(); if (%subs){ my $ncur = $pkg; print("$ncur ==>\n"); foreach my $ok (keys %subs){ my $nk = $ok; $nk =~ s/$pkg\:\://; print "$pkg->$ok => $nk\n"; $subs{$nk} = $subs{$ok}; delete $subs{$ok}; } $modinfos{$pkg}{subs} = \%subs; foreach my $sub (sort keys %subs){ _showcode($pkg, $sub); } } push @done, $pkg; } } my $fh = new IO::File(); $fh->Introspect;

Replies are listed 'Best First'.
Re: Find the real code for a subroutine ref?
by Fletch (Bishop) on Jan 02, 2007 at 23:46 UTC

    As you're finding this isn't something trivial. You might have more luck with your current approach to find the file and then use PPI to parse the file contents.

    You can always cheat with something like this kludge I came up with a while ago. :)

    Update: Oop, pointed to the parent of that question, not my kludge; fixed link.

Re: Find the real code for a subroutine ref?
by Limbic~Region (Chancellor) on Jan 03, 2007 at 01:15 UTC
Re: Find the real code for a subroutine ref?
by diotalevi (Canon) on Jan 03, 2007 at 16:58 UTC

    I've no interest in running your code right now and would like you to post an example of your input, the actual output, and the expected output. That's de rigur fore most "help me!" nodes, why not yours?

    [Updated: I see. You're getting line numbers from Module::Info and then attempting to parse perl within that. That approach won't work. Just use PPI or look into Larry's MADPROPS extension to bleadperl.]

    ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊