weird bug

K

kj

The following short module illustrates a puzzling bug:

use strict;
package Foo;
my $x = +{ 1 => 'one' };

sub foo {
keys %$x if 0; # should do nothing
my $y = eval '$x'; die if $@;
printf "%d\n", scalar keys %$y;
}

1;
__END__


OK, now: the following one-liner produces the expected output:

% perl -MFoo -e 'Foo::foo()'
1

....but if one comments-out the first line of Foo::foo, the output changes:

% perl -MFoo -e 'Foo::foo()'
0

Note that the line that was commented out should do nothing, because
it ends with the conditional qualifier "if 0". Therefore, this
line is essentially a no-op and should have absolutely no effect
on the execution of this code.

I'm seeing this behavior with v. 5.8.8. Does anyone see it also
with more recent versions?

I have no doubt that this is a bug, but I can't begin to figure
out why it's happening. Any light that may be thrown on this puzzle
would be much appreciated.

Also, is there a better solution to this problem than including
silly "voodoo code" like the first line in Foo::foo?

TIA!

Kynn
 
X

xhoster

kj said:
The following short module illustrates a puzzling bug:

use strict;
package Foo;
my $x = +{ 1 => 'one' };

sub foo {
keys %$x if 0; # should do nothing
my $y = eval '$x'; die if $@;
printf "%d\n", scalar keys %$y;
}

1;
__END__

The appearance of $x in %$x causes the subroutine to take out and
hold a "reference" to $x. The appearance of $x in the string eval
doesn't cause that happen. I assume that when the __END__ is encountered,
the "my $x" goes out of scope, decrementing the refcount. In the one
case, the refcount is decremented to zero, and $x is freed. Later, when
the string eval is invoked, it operates on this freed $x.

Also, is there a better solution to this problem than including
silly "voodoo code" like the first line in Foo::foo?

Change
my $y = eval '$x';
to
my $y = eval {$x};

That way, the use of $x is not hidden from the subroutine so it knows to
keep a reference to $x.

Unless there is a good reason you were using '$x' instead of {$x} in the
first place. I can't think of such a reason off the top of my head.

Xho

--
-------------------- http://NewsReader.Com/ --------------------
The costs of publication of this article were defrayed in part by the
payment of page charges. This article must therefore be hereby marked
advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
this fact.
 
B

Ben Morrow

Quoth kj said:
The following short module illustrates a puzzling bug:

use strict;
package Foo;
my $x = +{ 1 => 'one' };

sub foo {
keys %$x if 0; # should do nothing
my $y = eval '$x'; die if $@;
printf "%d\n", scalar keys %$y;
}

1;
__END__


OK, now: the following one-liner produces the expected output:

% perl -MFoo -e 'Foo::foo()'
1

...but if one comments-out the first line of Foo::foo, the output changes:

% perl -MFoo -e 'Foo::foo()'
0

This can be reduced to

package Foo;

my $x = 1;

sub foo {
#$x;
my $y = eval '$x';
printf "from %s: %s\n",
scalar caller, defined $y ? $y : '<undef>';
}

foo;

1;

which gives

~% perl -MFoo -eFoo::foo
from Foo: 1
from main: <undef>

with all perls I have to hand (5.6, 5.8, 5.10, blead). This is a
longstanding bug in eval STRING: it won't capture variables from an
outer lexical scope unless they are explicitly mentioned in the
immediately surrounding scope. Any mention, such as the line commented
out, is sufficient.

What I don't understand is 1. why it works when called from the same
file and 2. I though this had been fixed in 5.10. I guess not...
Also, is there a better solution to this problem than including
silly "voodoo code" like the first line in Foo::foo?

Fix perl's eval-handling to work properly? :)

Ben
 
X

xhoster

Ben Morrow said:
This can be reduced to

package Foo;
my $x = 1;
sub foo {
#$x;
my $y = eval '$x';
printf "from %s: %s\n",
scalar caller, defined $y ? $y : '<undef>';
}
foo;
1;

which gives

~% perl -MFoo -eFoo::foo
from Foo: 1
from main: <undef>

with all perls I have to hand (5.6, 5.8, 5.10, blead). This is a
longstanding bug in eval STRING: it won't capture variables from an
outer lexical scope unless they are explicitly mentioned in the
immediately surrounding scope. Any mention, such as the line commented
out, is sufficient.

What I don't understand is 1. why it works when called from the same
file

When it is called in the same file, the variable is still "in scope" in its
own right because the file scope has not yet ended. Once the file
scope has ended, the variable contents are destroyed unless something else
(like a subroutine stuffed into the symbol table) keeps it alive.

By replacing file-scope with extra curlies, you can get the same thing
without using packages.

perl -le ' {my $x=1; sub foo { print eval q{$x}}; foo; } foo;'

I don't see how this could be fixed. Either you parse the eval string
at compile time (which is not usually possible--if the string can't
change between compile time and run time, why use string eval rather than
block eval in the first place?), or you don't do garbage collection on any
lexical variable which happens to be in scope when a string eval is
encountered during the compilation pass. Or maybe when using a variable
whose contents have already been freed should throw an error rather than
treating it as undefined.

Xho

--
-------------------- http://NewsReader.Com/ --------------------
The costs of publication of this article were defrayed in part by the
payment of page charges. This article must therefore be hereby marked
advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
this fact.
 
B

Ben Morrow

Quoth (e-mail address removed):
When it is called in the same file, the variable is still "in scope" in its
own right because the file scope has not yet ended. Once the file
scope has ended, the variable contents are destroyed unless something else
(like a subroutine stuffed into the symbol table) keeps it alive.

Yup. Thank you: I was confusing locating an entry in the lexical pad
with refcounting of the contents of that entry.

Ben
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top