Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

perl string interpolation when using the package delimiter

Tags:

perl

Am fighting some legacy perl looking like the following:

sub UNIVERSAL::has_sub_class {
    my ($package,$class) = @_;
    my $all = all_packages();
    print "$package - $class", "\n";
    print "$package::$class", "\n";
    return exists $all->{"$package::$class"};
}

On two different systems, two different PERL installations / versions, this code behaves differently, i.e. the "$package::$class" construct is properly resolved to the correct package name on one system, but not on the other.

The following different print outputs can be seen when running has_sub_class on the two different systems:

# print output on system 1 (perl v5.8.6):
webmars::parameter=HASH(0xee93d0) - webmars::parameter::date
webmars::parameter::date

# print output on system 2 (perl v5.18.1):
webmars::parameter=HASH(0x251c500) - webmars::parameter::date
webmars::parameter=HASH(0x251c500)::webmars::parameter::date

Has there been any string interpolation changes in between perl v5.8.6 and perl v5.18.1 that you know might cause this behaviour ? Or should I look somewhere else ? I've really tried google-ing around and reading through the perl change notes, but could not find anything of interest.

With my limited knowledge of perl, I've tried getting the smallest piece of code that could reproduce the problem I am having. I have come up with the following which I hope is relevant:

# system 1 (perl v5.8.6):
$ perl -e 'my %x=(),$x=bless(\%x),$y='bar';print "$x::$y\n";'
bar

# system 2 (perl v5.18.1):
$ perl -e 'my %x=(),$x=bless(\%x),$y='bar';print "$x::$y\n";'
main=HASH(0xec0ce0)::bar

The outputs are different ! Any ideas ?

like image 527
Cricri Avatar asked May 12 '15 14:05

Cricri


2 Answers

Shorter demonstration:

($x::, $x) = (1,2);  print "$x::$x"

$ perl5.16.3 -e '($x::, $x) = (1,2);  print "$x::$x"'
12

$ perl5.18.1 -e '($x::, $x) = (1,2);  print "$x::$x"'
2::2

Getting warmer.

$ perl5.16.3 -MO=Concise =e 'print "$x::$x"'
8  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 1 -e:1) v:{ ->3
7     <@> print vK ->8
3        <0> pushmark s ->4
-        <1> ex-stringify sK/1 ->7
-           <0> ex-pushmark s ->4
6           <2> concat[t3] sK/2 ->7
-              <1> ex-rv2sv sK/1 ->5
4                 <#> gvsv[*x::] s ->5          <-     $x::
-              <1> ex-rv2sv sK/1 ->6
5                 <#> gvsv[*x] s ->6            <-     $x
-e syntax OK

$ perl5.18.1 -MO=Concise -e 'print "$x::$x"'
a  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 1 -e:1) v:{ ->3
9     <@> print vK ->a
3        <0> pushmark s ->4
-        <1> ex-stringify sK/1 ->9
-           <0> ex-pushmark s ->4
8           <2> concat[t4] sKS/2 ->9
6              <2> concat[t2] sK/2 ->7
-                 <1> ex-rv2sv sK/1 ->5
4                    <#> gvsv[*x] s ->5         <-     $x
5                 <$> const[PV "::"] s ->6      <-     "::"
-              <1> ex-rv2sv sK/1 ->8
7                 <#> gvsv[*x] s ->8            <-     $x
-e syntax OK

TL;DR. v5.16 parses "$x::$x" as $x:: . $x. v5.18 as $x . "::" . $x. I don't see any obvious reference to this change in the delta docs, but I'll keep looking.

like image 80
mob Avatar answered Sep 23 '22 12:09

mob


So, my very quick test confirms the issue - using

perl -Mstrict -we 'my %x=(),$x=bless(\%x),$y="bar";print "$x::$y\n";'

(With your version, I get a bareword warning for 'bar').

The error I get in 5.8.8 is "use of uninitialized value in concatenation".

The difference seems to be that when I run with perl -MO=Deparse I get:

my ( %x ) = ();
my $x = bless ( \%x );
my $y = 'bar';
print "$x::$y\n";

If I run on 5.20.2 though, I get:

my ( %x ) = ();
my $x = bless ( \%x );
my $y = 'bar';
print "${x}::$y\n";

So yes, there has been a change in how the same code is parsed. But I'm not entirely sure how that helps you, apart from perhaps enlightening you as to what's going on?

like image 34
Sobrique Avatar answered Sep 22 '22 12:09

Sobrique