Ещё один способ отстрелить себе ногу в Perl

в 17:32, , рубрики: perl, ошибки в коде, Программирование, метки: ,

Посмотрим на код:

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)

В чём же дело?

  1. в Perl (как и во многих других языках) X && Y, в случае если X — false, возвратит не просто false (не выполняя Y), а возвратит сам X (который должен быть false)
  2. Все параметры в 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

Источник

Поделиться

* - обязательные к заполнению поля