Is there a tool for extracting all variable, module, and function names from a Perl module file?

StackOverflow https://stackoverflow.com/questions/1343630

  •  20-09-2019
  •  | 
  •  

Question

My apologies if this is a duplicate; I may not know the proper terms to search for.

I am tasked with analyzing a Perl module file (.pm) that is a fragment of a larger application. Is there a tool, app, or script that will simply go through the code and pull out all the variable names, module names, and function calls? Even better would be something that would identify whether it was declared within this file or is something external.

Does such a tool exist? I only get the one file, so this isn't something I can execute -- just some basic static analysis I guess.

Was it helpful?

Solution

Check out the new, but well recommended Class::Sniff.

From the docs:

use Class::Sniff;
my $sniff = Class::Sniff->new({class => 'Some::class'});

my $num_methods = $sniff->methods;
my $num_classes = $sniff->classes;
my @methods     = $sniff->methods;
my @classes     = $sniff->classes;

{
  my $graph    = $sniff->graph;   # Graph::Easy
  my $graphviz = $graph->as_graphviz();

  open my $DOT, '|dot -Tpng -o graph.png' or die("Cannot open pipe to dot: $!");
  print $DOT $graphviz;
}

print $sniff->to_string;
my @unreachable = $sniff->unreachable;
foreach my $method (@unreachable) {
    print "$method\n";
}

This will get you most of the way there. Some variables, depending on scope, may not be available.

OTHER TIPS

If I understand correctly, you are looking for a tool to go through Perl source code. I am going to suggest PPI.

Here is an example cobbled up from the docs:

#!/usr/bin/perl

use strict;
use warnings;

use PPI::Document;
use HTML::Template;

my $Module = PPI::Document->new( $INC{'HTML/Template.pm'} );

my $sub_nodes = $Module->find(
    sub { $_[1]->isa('PPI::Statement::Sub') and $_[1]->name }
);

my @sub_names = map { $_->name } @$sub_nodes;

use Data::Dumper;
print Dumper \@sub_names;

Note that, this will output:

     ...
     'new',
     'new',
     'new',
     'output',
     'new',
     'new',
     'new',
     'new',
     'new',
     ...

because multiple classes are defined in HTML/Template.pm. Clearly, a less naive approach would work with the PDOM tree in a hierarchical way.

Another CPAN tools available is Class::Inspector

use Class::Inspector;

# Is a class installed and/or loaded
Class::Inspector->installed( 'Foo::Class' );
Class::Inspector->loaded( 'Foo::Class' );

# Filename related information
Class::Inspector->filename( 'Foo::Class' );
Class::Inspector->resolved_filename( 'Foo::Class' );

# Get subroutine related information
Class::Inspector->functions( 'Foo::Class' );
Class::Inspector->function_refs( 'Foo::Class' );
Class::Inspector->function_exists( 'Foo::Class', 'bar' );
Class::Inspector->methods( 'Foo::Class', 'full', 'public' );

# Find all loaded subclasses or something
Class::Inspector->subclasses( 'Foo::Class' );

This will give you similar results to Class::Sniff; you may still have to do some processing on your own.

There are better answers to this question, but they aren't getting posted, so I'll claim the fastest gun in the West and go ahead and post a 'quick-fix'.

Such a tool exists, in fact, and is built into Perl. You can access the symbol table for any namespace by using a special hash variable. To access the main namespace (the default one):

for(keys %main::) { # alternatively %::
  print "$_\n";
}

If your package is named My/Package.pm, and is thus in the namespace My::Package, you would change %main:: to %My::Package:: to achieve the same effect. See the perldoc perlmod entry on symbol tables - they explain it, and they list a few alternatives that may be better, or at least get you started on finding the right module for the job (that's the Perl motto - There's More Than One Module To Do It).

If you want to do it without executing any code that you are analyzing, it's fairly easy to do this with PPI. Check out my Module::Use::Extract; it's a short bit of code shows you how to extract any sort of element you want from PPI's PerlDOM.

If you want to do it with code that you have already compiled, the other suggestions in the answers are better.

I found a pretty good answer to what I was looking for in this column by Randal Schwartz. He demonstrated using the B::Xref module to extract exactly the information I was looking for. Just replacing the evaluated one-liner he used with the module's filename worked like a champ, and apparently B::Xref comes with ActiveState Perl, so I didn't need any additional modules.

perl -MO=Xref module.pm 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top