'转到undefined subroutine&main :: 1'编写一个简单的Perl调试器

she*_*edd 2 debugging perl perl-module

我正在尝试编写一个简单的Perl调试器,我遇到了以下问题.

我正在运行以下代码作为调试器:

{
  package DB;

  sub DB { }

  sub sub
  {
    &$sub;

    # this is what produces the problem
    $i = 1*1;
  }
}

1;
Run Code Online (Sandbox Code Playgroud)

我通过设置PERL5DB环境变量来加载它 - 例如:

export PERL5DB ="BEGIN {require'./debugger/tracer.pl';}

鉴于这个简单的小Perl脚本:

#!/usr/bin/env perl

use Getopt::Long;

print "hello world";
Run Code Online (Sandbox Code Playgroud)

我正在运行脚本:

perl -d test.pl

运行时,会生成以下错误:

$ perl -d test.pl
Goto undefined subroutine &main::1 at /home/vagrant/perl5/perlbrew/perls/perl-5.16.0/lib/site_perl/5.16.0/Exporter.pm line 25.
BEGIN failed--compilation aborted at test.pl line 6.
Run Code Online (Sandbox Code Playgroud)

我把问题分离到了&$ sub之后运行的任何东西; 在调试器中调用sub.这个问题发生在基本Perl脚本中包含的某些包 - 在这种情况下,Getopt :: Long,尽管我也在IO :: File中找到了相同的结果.

我的Perl非常生疏,特别是在调试器等高级主题方面.

任何人都可以帮助我理解如何在&$ sub之后执行代码执行; 在调试器中调用sub以正确放置我正在导入的包?

谢谢!

Ilm*_*nen 6

当您在不使用显式return语句的情况下保留Perl子例程时,Perl将返回子例程中最后一个语句的值.

特别是,这意味着如果你有一个子程序将另一个子程序作为最后一个语句调用,如下所示:

package DB {
    sub sub {
        warn "Hello from DB::sub, about to call $sub\n";
        &$sub;
    }
}
Run Code Online (Sandbox Code Playgroud)

那么调用via的另一个子例程的返回值&$sub将被传递给原始调用者,就像你做了一个显式的一样return &$sub.

但是,如果&$sub调用不是DB::sub子例程中的最后一件事,那么Perl将抛弃其返回值,而是返回实际最后一个语句的值 - 在这种情况下$i = 1*1,其值为1.

现在,当您定义这样的自定义调试器时,Perl将通过调用您的子例程来包装每个普通的子例程调用DB::sub.因此,您的代码会导致每个子例程调用返回数字1!毫不奇怪,这会打破许多非常糟糕的事情.

具体来说,根据您的错误消息,它看起来像Exporter模块(许多其他模块用于将符号导出到调用者的命名空间)正在调用一个子例程,该子例程应该返回对另一个子例程的引用.但是,由于你的调试器,它实际上返回1,以下尝试调用返回的子例程最终试图调用一个名为的子例程1(main::由于数字符号名称是超全局而被映射到包),然后失败.


但是,如果你真的需要在你的电话DB::sub 做一些事情&$sub呢?好吧,解决方法是保存返回值,如下所示:

package DB {
    sub DB { }
    sub sub {
        warn "Hello from DB::sub, about to call $sub...\n";

        # call &sub, save the return value in @rv
        my @rv = (wantarray ? &$sub : scalar &$sub);

        warn "Hello again from DB::sub, just called $sub and got @rv!\n";

        # ...and return the saved return value
        return (wantarray ? @rv : $rv[0]);
    }
}

1;
Run Code Online (Sandbox Code Playgroud)

(代码有点复杂,因为我们DB::sub可能会在列表或标量上下文中调用它们,并且我们需要将适当的上下文传递给&$sub.但wantarray应该注意这一点.)