如何为 Perl 制作静态分析调用图? [英] How can I make a static analysis call graph for Perl?

查看:23
本文介绍了如何为 Perl 制作静态分析调用图?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个中等复杂的 Perl 程序.作为其开发的一部分,它必须经过修改和测试.由于某些环境限制,经常运行这个程序并不是一个容易练习的选项.

I am working on a moderately complex Perl program. As a part of its development, it has to go through modifications and testing. Due to certain environment constraints, running this program frequently is not an option that is easy to exercise.

我想要的是 Perl 的静态调用图生成器.它不必涵盖所有边缘情况(例如,在 eval 中将变量重新定义为函数,反之亦然).

What I want is a static call-graph generator for Perl. It doesn't have to cover every edge case(e,g., redefining variables to be functions or vice versa in an eval).

(是的,我知道 Devel::DprofPP 有一个运行时调用图生成工具,但不能保证运行时调用每个函数.我需要能够看看每个函数.)

(Yes, I know there is a run-time call-graph generating facility with Devel::DprofPP, but run-time is not guaranteed to call every function. I need to be able to look at each function.)

推荐答案

一般情况下无法做到:

my $obj    = Obj->new;
my $method = some_external_source();

$obj->$method();

但是,获取大量案例应该相当容易(针对自身运行此程序):

However, it should be fairly easy to get a large number of the cases (run this program against itself):

#!/usr/bin/perl

use strict;
use warnings;

sub foo {
    bar();
    baz(quux());
}

sub bar {
    baz();
}

sub baz {
    print "foo\n";
}

sub quux {
    return 5;
}

my %calls;

while (<>) {
    next unless my ($name) = /^sub (\S+)/;
    while (<>) {
        last if /^}/;
        next unless my @funcs = /(\w+)\(/g;
        push @{$calls{$name}}, @funcs;
    }
}

use Data::Dumper;
print Dumper \%calls;

注意,这会漏掉

  • 调用不使用括号的函数(例如 print "foo\n";)
  • 调用被取消引用的函数(例如 $coderef->())
  • 调用字符串方法(例如 $obj->$method())
  • 将推杆称为另一行的左括号
  • 其他我没有想到的事情

它错误地捕捉

  • 注释函数(例如 #foo())
  • 一些字符串(例如"foo()")
  • 其他我没有想到的事情

如果你想要一个比那毫无价值的黑客更好的解决方案,是时候开始研究 PPI,但即使它也会有诸如 $obj->$method() 之类的问题.

If you want a better solution than that worthless hack, it is time to start looking into PPI, but even it will have problems with things like $obj->$method().

只是因为无聊,这里有一个使用的版本PPI.它只查找函数调用(而不是方法调用).它也不会尝试保持子例程的名称唯一(即,如果您多次调用同一个子例程,它将出现多次).

Just because I was bored, here is a version that uses PPI. It only finds function calls (not method calls). It also makes no attempt to keep the names of the subroutines unique (i.e. if you call the same subroutine more than once it will show up more than once).

#!/usr/bin/perl

use strict;
use warnings;

use PPI;
use Data::Dumper;
use Scalar::Util qw/blessed/;

sub is {
    my ($obj, $class) = @_;
    return blessed $obj and $obj->isa($class);
}

my $program = PPI::Document->new(shift);

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

die "no subroutines declared?" unless $subs;

for my $sub (@$subs) {
    print $sub->name, "\n";
    next unless my $function_calls = $sub->find(
        sub { 
            $_[1]->isa('PPI::Statement')             and
            $_[1]->child(0)->isa("PPI::Token::Word") and
            not (
                $_[1]->isa("PPI::Statement::Scheduled") or
                $_[1]->isa("PPI::Statement::Package")   or
                $_[1]->isa("PPI::Statement::Include")   or
                $_[1]->isa("PPI::Statement::Sub")       or
                $_[1]->isa("PPI::Statement::Variable")  or
                $_[1]->isa("PPI::Statement::Compound")  or
                $_[1]->isa("PPI::Statement::Break")     or
                $_[1]->isa("PPI::Statement::Given")     or
                $_[1]->isa("PPI::Statement::When")
            )
        }
    );
    print map { "\t" . $_->child(0)->content . "\n" } @$function_calls;
}

这篇关于如何为 Perl 制作静态分析调用图?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆