Посмотрим на код:
use strict;
use warnings;
sub mysub($$)
{
my ($a, $b) = @_;
print "$an";
print "$bn";
}
my $x = undef;
mysub($x && $x->[0] =~ /abc/, $x = []);
Может ли mysub в качестве первого аргумента получить нечто, что в boolean контексте является истинной?
Казалось бы нет, ведь для этого $x должен быть ссылкой на массив, а его первый элемент должен содержать подстроку 'abc'
Но оказалось, что может
$perl poc.pl
ARRAY(0xdb6d48)
ARRAY(0xdb6d48)
В чём же дело?
- в Perl (как и во многих других языках) X && Y, в случае если X — false, возвратит не просто false (не выполняя Y), а возвратит сам X (который должен быть false)
- Все параметры в Perl передаются по ссылке, а не по значению.
Таким образом когда расчитывается первый аргумент
$x && $x->[0] =~ /abc/
$x является undef т.е. false, следовательно вместо первого аргумента передаётся сам $x,
когда расчитывается второй аргумент, $x присваивается значение [] (ссылка на пустой массив), таким образом когда начинает выполняться код mysub, оба аргумента
содежрат ссылку на $x (которая является пустым массивом)
Если кажется, что код выглядит не очень применимым на практике, то могу сказать что баг был найден в реальном коде unit теста:
ok( ($errors && ($errors->[0] =~ /My Exception/i)), "should catch exception $errors->[0]" );
(при этом срабатывает ещё одна замечательная фича перла Autovivification — если обратиться к $errors->[0] то $error->[0] создастся,
если $error был до этого undef)
Автор: vsespb