Perl';s caller()函数返回不正确的行号

Perl';s caller()函数返回不正确的行号,perl,Perl,我在Perl 5.10.1上运行了以下脚本: #!/usr/bin/perl use strict; use warnings; foreach( my $x =0 ; $x < 1; $x++) { # Line 5 print_line(); # Line 6 } sub print_line { print "Function call from line: " . [caller(0)]->[2] . "\n";

我在Perl 5.10.1上运行了以下脚本:

#!/usr/bin/perl
use strict;
use warnings;

foreach( my $x =0 ; $x < 1; $x++) {   # Line 5
  print_line();                       # Line 6
} 

sub print_line {
  print "Function call from line: " . [caller(0)]->[2] . "\n";
}

真正奇怪的是,如果我把一个随机语句扔到C循环中的一个空行中,<代码>呼叫者< /代码>返回正确的行号:

#!/usr/bin/perl
use strict;
use warnings;

foreach( my $x =0 ; $x < 1; $x++) {
  my $x = 3;
  print_line();  # Line 7
}

sub print_line {
  print "Function call from line: " . [caller(0)]->[2] . "\n";
}

这是某种错误还是我可以做些什么让调用方准确地报告行号?

我怀疑这可能是由于语句分隔符(分号)造成的。正如您可能已经发现的那样,对于正在运行的代码,
caller
报告的行号与
foreach
循环相同

所以我认为发生的事情,是因为没有分号

如果要执行多行子呼叫,
呼叫方
将报告第一行:

print "first call:", __LINE__, "\n";
print "Start of statement\n",
"a bit more on line ", __LINE__, "\n",
print_line(
    1,

    2,

    3,

    5,

);
你得到的是电话开始的号码,而不是结束的号码。因此,我认为这就是您得到的结果-语句在分号语句分隔符出现时开始-这是第一个示例中的
foreach

因此,作为一种解决方法,我可能建议使用。虽然我也可能建议不要太担心它,因为它仍然会将您指向代码中正确的位置


如果您使用
croak
,可能出于同样的原因,您会得到类似的结果

我认为这可能是一个bug,因为如果您替换

foreach (my $x = 0 ; $x < 1 ; $x++) {
我不清楚到底发生了什么,但通过比较两个不同版本的操作树,我认为
nextstate
op得到了不正确的优化。我的版本有

<;> nextstate(main 4 lineno.pl:11) v:*,&,x*,x&,x$,$ ->8
已从执行流程中删除

把它写成一个。

$code>$perl-MO=concrete a.pl并不难 j离开[1参考]vKP/REFC->(结束) 1输入->2 下一个状态(main 6 a.pl:5)v:*,&,{,x*,x&,x$,$->3 5.设计vKS/2->6 3常数[IV 0]s->4 4 padsv[$x:3,5]sRM*/LVINTRO->5 6取消堆叠v*->7 i离开循环vK/2->j 7进入循环(下一步->上一步->我重做->8)v->e -空vK/1->i h和(其他->8)vK/1->i g lt sK/2->h e padsv[$x:3,5]s->f f常数[IV 1]s->g -lineseq vK->- -范围vK->b 8 b -前列表K->a 8推动标记s->9 -ex-rv2cv sK/2->- 9 gv[*打印行]s/EARLYCV->a c preinc[t2]vK/1->d b padsv[$x:3,5]sRM->c d取消堆叠v->e a、 pl语法正常 正在进行一些优化。
范围被认为是不必要的,并被优化掉了。(注意“
-
”意味着它从未达到。)

但同时,这删除了
nextstate
op,它为警告和
调用方设置行号


因此,这是一个错误,是由于不适当的优化造成的。

正如已经指出的,这确实是Perl中的一个错误,至少可以追溯到5.10或11年前,但实际上我认为时间更长

据报道,这是一个Perl错误,尽管据称修复起来并不困难,但事实并非如此。修复起来可能也不那么容易,因为添加COP会降低性能,可能还需要一些管理工作来调整测试

即使这个错误被修复了,它也只能在Perl5.29和更高版本中修复,这对5.10没有帮助

因此,这里还有另一个策略,它不依赖于对Perl核心的更改,因此可以让用户更好地控制。然而,我要说的是,在前面它有点实验性,除非人们愿意在它上花费编码的精力,否则它不太可能追溯到5.10。现在我使用的最早的Perl版本是5.14,7年前,好像没有他的作品

使用可以编写不同的调用程序,我认为更好的调用程序()可以更详细地显示调用程序的位置。下面是修改后的程序:

#!/usr/bin/perl
use strict;
use warnings;
use B::DeparseTree::Fragment;
use Devel::Callsite;

sub dt_caller
{
    my $level = $_ ? $_ : 0;
    # Pick up the right caller's OP address.
    my $addr =  callsite($level+1);
    # Hack alert 'main::main' should be replaced with the function name if not the top level. caller() is a little off-sync here.
    my $op_info = deparse_offset('main::main', $addr);
    # When Perl is in the middle of call, it has already advanced the PC,
    # so we need to go back to the preceding op. 
    $op_info = get_prev_addr_info($op_info);
    my $extract_texts = extract_node_info($op_info);
    print join("\n", @$extract_texts), "\n";
}

foreach( my $x =0 ; $x < 1; $x++) {
  print_line();
}

sub print_line {
    dt_caller();
}

dt_caller()可以而且应该像Carp那样包装成一个包,这样你就看不到所有的丑陋。但是我会把它留给其他人。我要提到的是,为了让它工作,我必须做一些错误修复,所以这只在B::DeparseTree的3.4.0版开始工作。

有趣-是的,似乎是这样。看起来就像它返回“循环”的行号一样。
\uuuu line\uuuuuu
宏可能有助于故障排除。很好。很有趣。你用什么来提取代码的优化?虽然我看不到它是写出来的,但看起来这是一个已经修复的错误。Perl5版本20没有显示这种行为。@Borodin有趣,我怀疑它是错误当时我做的第一件事就是用5.20进行测试,我仍然看到了。我想知道有什么不同。我在一个Windows上运行了我的,而你可能在Linux上?尽管我无法想象这会有什么不同。
foreach my $x (0 .. 0) {
<;> nextstate(main 4 lineno.pl:11) v:*,&,x*,x&,x$,$ ->8
<0> ex-nextstate v ->8
$ perl -MO=Concise a.pl
j  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 6 a.pl:5) v:*,&,{,x*,x&,x$,$ ->3
5     <2> sassign vKS/2 ->6
3        <$> const[IV 0] s ->4
4        <0> padsv[$x:3,5] sRM*/LVINTRO ->5
6     <0> unstack v* ->7
i     <2> leaveloop vK/2 ->j
7        <{> enterloop(next->b last->i redo->8) v ->e
-        <1> null vK/1 ->i
h           <|> and(other->8) vK/1 ->i
g              <2> lt sK/2 ->h
e                 <0> padsv[$x:3,5] s ->f
f                 <$> const[IV 1] s ->g
-              <@> lineseq vK ->-
-                 <@> scope vK ->b                       <---
-                    <0> ex-nextstate v ->8              <---
a                    <1> entersub[t5] vKS/TARG,2 ->b
-                       <1> ex-list K ->a
8                          <0> pushmark s ->9
-                          <1> ex-rv2cv sK/2 ->-
9                             <#> gv[*print_line] s/EARLYCV ->a
c                 <1> preinc[t2] vK/1 ->d
b                    <0> padsv[$x:3,5] sRM ->c
d                 <0> unstack v ->e
a.pl syntax OK
#!/usr/bin/perl
use strict;
use warnings;
use B::DeparseTree::Fragment;
use Devel::Callsite;

sub dt_caller
{
    my $level = $_ ? $_ : 0;
    # Pick up the right caller's OP address.
    my $addr =  callsite($level+1);
    # Hack alert 'main::main' should be replaced with the function name if not the top level. caller() is a little off-sync here.
    my $op_info = deparse_offset('main::main', $addr);
    # When Perl is in the middle of call, it has already advanced the PC,
    # so we need to go back to the preceding op. 
    $op_info = get_prev_addr_info($op_info);
    my $extract_texts = extract_node_info($op_info);
    print join("\n", @$extract_texts), "\n";
}

foreach( my $x =0 ; $x < 1; $x++) {
  print_line();
}

sub print_line {
    dt_caller();
}
$ perl bug-caller.pl
print_line()
------------