Perl的caller()函数返回错误的行号

时间:2015-02-21 23:21:39

标签: 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";
}

尽管调用了来自第6行的子程序,但脚本输出了C语句开头的行号for语句:

Function call from line: 5

如果我将一个随机语句放入C-style for循环中的空行之一,caller返回正确的行号,真是奇怪:

#!/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";
}

上面的脚本正确输出:

Function call from line: 7

这是某种错误还是我可以采取哪些措施让caller准确报告行号?

4 个答案:

答案 0 :(得分:7)

我认为这可能是一个错误,因为如果你替换

,同样的行为就不会发生
foreach (my $x = 0 ; $x < 1 ; $x++) {

foreach my $x (0 .. 0) {

我不明白确切地发生了什么,但通过比较两个不同版本的选项,我认为nextstate op正在进行不正确的优化出。我的版本有

<;> nextstate(main 4 lineno.pl:11) v:*,&,x*,x&,x$,$ ->8

作为调用entersub的{​​{1}}操作系统的左侧兄弟,而您的<{p>}

print_line

已从执行流程中取出。

将此作为perlbug写下来并不会有什么坏处。

答案 1 :(得分:5)

$ 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

正在进行一些优化。 scope被认为是不必要的并且已经过优化。 (注意“-”意味着它永远不会到达。)

但与此同时,删除了nextstate操作,这是设置警告和caller的行号的原因。

因此,这是一个由于不正确的优化而导致的错误。

答案 2 :(得分:2)

我怀疑这可能是声明分隔符(分号)。您可能已经发现 - 使用您正在运行的代码,caller报告的行号与foreach循环相同。

所以我发生了什么,是因为没有分号。

如果您要进行多行子通话,caller会报告第一行:

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

    2,

    3,

    5,

);

您获得了通话开始的行号,而不是结束。所以我认为这是你得到的 - 语句在分号语句分隔符出现时开始 - 这是第一个例子中的foreach行。

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

如果你使用croak,你会得到类似的东西,原因可能是同样的原因。

答案 3 :(得分:1)

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

据报道它是Perl错误perl #133239,尽管据称不是 难以解决,但事实并非如此。也可能不是容易容易修复,因为添加COP会减慢性能,因此性能会有所下降,并且可能需要一些管理工作来调整测试。

即使该错误已修复,也只能在Perl 5.29及更高版本中修复。这对5.10毫无帮助。

因此,这是另一种方法,它不依赖于Perl核心的更改,因此可以使用户更好地控制。但是,我会先说一下这是一个实验,除非人们愿意为此花费代码,否则它的时间不可能回到5.10。现在,我使用的最早的Perl版本是7年前的5.14,就像本文写作一样。

使用B::DeparseTree可以编写不同的代码,我认为更好的 caller()可以向您显示呼叫者的详细位置。这是您的程序经过修改以实现的目的:

#!/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()
------------

dt_caller()可以并且应该包装成Carp这样的包,这样您就不会看到所有的丑陋之处。但是,我会将其留给其他人使用。我要提到的是,为了使此功能正常运行,我必须进行一些错误修复,因此,该功能仅从 B :: DeparseTree 的3.4.0版本开始起作用。