我正在尝试编写一个简单的Perl调试器,我遇到了以下问题。
我正在运行以下代码作为调试器:
{
package DB;
sub DB { }
sub sub
{
&$sub;
# this is what produces the problem
$i = 1*1;
}
}
1;
我通过设置PERL5DB环境变量来加载它 - 例如:
export PERL5DB =“BEGIN {require'./debugger/tracer.pl';}
鉴于这个简单的小Perl脚本:
#!/usr/bin/env perl
use Getopt::Long;
print "hello world";
我正在运行脚本:
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.
我已将问题隔离到调试器中& $ sub; 调用sub后运行的任何内容。在基本Perl脚本中包含某些包时会出现此问题 - 在本例中为Getopt :: Long,尽管我也在IO :: File中找到了相同的结果。
我的Perl非常生疏,特别是在调试器等高级主题方面。
任何人都可以帮助我理解如何在调试器中的& $ sub; 调用sub之后让代码执行,以便与我正在导入的包很好地放在一起?
谢谢!
答案 0 :(得分:6)
当您在不使用显式return
语句的情况下离开Perl子例程时,Perl将返回子例程中最后一个语句的值。
特别是,这意味着如果你有一个子程序将另一个子程序作为最后一个语句调用,如下所示:
package DB {
sub sub {
warn "Hello from DB::sub, about to call $sub\n";
&$sub;
}
}
然后通过&$sub
调用的另一个子例程的返回值将传递给原始调用者,就像您已经完成了return &$sub
一样。
但是,如果&$sub
调用不是 DB::sub
子例程中的最后一件事,那么Perl只会丢弃其返回值而是返回你的值实际的最后一个语句 - 在本例中为$i = 1*1
,其值为1。
现在,当你define a custom debugger这样时,Perl将通过调用DB::sub
子例程来包装每个普通子例程调用。因此,您的代码会导致每个子例程调用返回数字1! 非常严重破坏很多事情并不奇怪。
具体来说,基于您的错误消息,它看起来像Exporter模块(许多其他模块用于将符号导出到调用者的命名空间)中的某个东西正在调用应该>的子例程em>返回对另一个子例程的引用。但是,由于你的调试器,它实际上返回1,以下尝试调用返回的子例程最终试图调用一个名为1
的子例程(它被映射到main::
包,因为{{3 }}),然后失败。
但是,在调用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;
(由于我们的DB::sub
可能在列表或标量上下文中被调用,因此代码有点复杂,我们需要将适当的上下文传递给&$sub
。{{1}但是应该照顾它。)
答案 1 :(得分:2)
加上Ilmari Karonen的答案。
DB :: sub也可以在无值(void)上下文中调用,因此返回处理需要考虑到这一点。有关详细信息,请参阅wantarray中的文档。
以下代码处理所有三种情况。
package DB {
sub DB { }
sub sub {
# call &sub, save the return value in @rv
my @rv;
if(defined(wantarray)) {
@rv = (wantarray ? &$sub : scalar &$sub);
}
else {
# wantarray is undef
&$sub;
}
# after invoking &$sub
# return @rv
if(defined(wantarray)) {
return (wantarray ? @rv : $rv[0]);
}
else {
return undef
}
}
}
1;