Функция caller () 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-style 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 ответов
$ 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
происходит некоторая оптимизация. The scope
был признан ненужным и оптимизирован. (Обратите внимание на "-
" это означает, что он никогда не достигнет.)
но в то же время, что удалены nextstate
op, который устанавливает номер строки для предупреждений и для caller
.
так, это ошибка, которая является результатом неправильной оптимизации.
Я думаю, что потенциально это ошибка, потому что такое же поведение не возникает, если вы замените
foreach (my $x = 0 ; $x < 1 ; $x++) {
С
foreach my $x (0 .. 0) {
Я не понимаю ровно что происходит, но сравнивая optrees двух разных версий, я думаю, что a nextstate
op неправильно оптимизирован. Моя версия
<;> nextstate(main 4 lineno.pl:11) v:*,&,x*,x&,x$,$ ->8
как левый брат entersub
op, который вызывает print_line
, в то время как ваш
<0> ex-nextstate v ->8
, который имеет был выведен из потока казни.
не помешало бы записать это как perlbug.
Я подозреваю, что это может быть вплоть до разделителей операторов (точек с запятой). Как вы, возможно, заметили - с кодом, который вы используете, номер строки, сообщенный 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
, по-видимому, то же причина.
Как было указано, это действительно ошибка в Perl, возвращающаяся по крайней мере к 5.10 или 11 годам, но на самом деле я думаю дольше.
было сообщено как ошибка Perl на Perl #133239 и хотя утверждается, что это не это трудно исправить, это не было. Может быть, это и не так!--7-->легко чтобы исправить, имеет последствия для производительности, так как добавление COP замедляет работу, и, возможно, потребуется некоторая административная работа для корректировки тестов.
и даже если бы эта ошибка была исправлена, она была бы исправлена только в версиях Perl 5.29 и позже или около того. Это не поможет тебе с 5.10.
Итак, вот еще один ТЭК, который не полагается на изменение ядра Perl и, следовательно, больше контролирует пользователей. Тем не менее, я скажу, что это немного экспериментально, и если люди не готовы тратить на это усилия по кодированию, это вряд ли вернется к 5.10. Сейчас ранние версии Perl у меня работает 5.14, 7 лет назад, как будто этого письма.
используя 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() может и должен быть завернут в пакет, как карп, чтобы вы не видели все это уродство. Однако я оставлю это для кого-то еще. И я упомяну, что просто, чтобы получить эту работу, были некоторые исправления ошибок, которые я должен был сделать, поэтому это работает только начиная с версии 3.4.0 B:: DeparseTree.