develooper Front page | perl.perl5.porters | Postings from December 2004

Re: [perl #32967] Re: More B bugs: svref_2object

Thread Previous
Stephen McCamant
December 31, 2004 15:07
Re: [perl #32967] Re: More B bugs: svref_2object
Message ID:
>>>>> "AT" == Alexey Tourbin <> writes:

AT> Brief description: consequent svref_2object calls produce wrong
AT> B objects in certain cases.  Detailed explanation and test cases
AT> are available via the following links:
SMcC> I think your diagnosis is correct. The B module in general, and
SMcC> svref_2object in particular, does not increase the reference
SMcC> count of the SV and OP objects the B::SV and B::OP proxies point
SMcC> to, so bad things happen if the proxy is used after the
SMcC> underlying object is freed. (I bet with more experimentation you
SMcC> could get a segfault, for instance).

AT> Okay, thanks a lot for this clarification.  Unfortunately I don't quite
AT> grok reference counting mechanism in Perl (for now).  I just have a code
AT> that has a kludge that apparently fixes the mechanism for perl-5.8.x.
AT> The code is as follows:

AT> [...] [snip]

AT> To examine the real code, you may want to take a look at
AT> (only PVMG version
AT> strings seems to break things).  My original post has a very
AT> simple example, though.

I think I can explain more easily what's going on in your simple
example. For the readers playing along at home, your short example

AT> my $v1 = 0.17;
AT> my $v2 = 0.1.7;
AT> sub get_sv {
AT>     my $v = shift;
AT>     my $sv = svref_2object(\$v);
AT>     return $sv;
AT> }
AT> Dump get_sv($v1);
AT> 	 get_sv($v2);
AT> Dump get_sv($v1);

The first Dump shows that its argument is a reference to a B::NV
object, while the second shows its to be a reference to a B::PVMG.

What I think may be confusing about this example is that get_sv
doesn't actually tell you about its argument SV. Rather, it returns a
B::SV object that describes its local $v variable. When, as in the
unmodified example, no reference to $v escapes the subroutine (the
reference count of $v is 1 on leaving the subroutine), perl doesn't
bother to free the local; rather, it keeps it around to reuse the next
time the subroutine is called. This allows it to save time that would
otherwise be spent reallocating and "upgrading" the local on the next
sub call; it assumes that if you made an SV big and complicated on one
invocation, you likely will again.

Thus all three calls to get_sv return B::SV objects referring to the
same SV. When a PVMG is passed in the second get_sv() call, $v is
upgraded to accommodate it, and it stays upgraded for the third call. 

If you change the code to pass the variable by reference, i.e.

sub get_sv {
        my $vref = shift;
        my $sv = svref_2object($vref);
        return $sv;
Dump    get_sv(\$v1);
Dump    get_sv(\$v1);

you'll see that $v1 stays an NV.

To sharpen what I was saying above, I don't think your bug comes from
an SV being freed to soon; rather, the SV is being reused
unexpectedly. Making another reference to the variable is a workaround
because it disables the reuse optimization; perl allocates a new SV,
which your code handles better.

On to how you might fix your real bug (note I've added a bit more
context in the first sub):

AT> sub extract_version {
AT>         [...] 
AT>         my $version = ....;
AT>         [...]
AT>         use B qw(svref_2object); 
AT>         our $perlbug32967 = \$version; 
AT>         my $v = sv_version(svref_2object(\$version)); 
AT>         # process $v 
AT> [...] 
AT> use B qw(class); 
AT> sub sv_version ($) { 
AT>         my $sv = shift; 
AT>         my $class = class($sv); 
AT>         if ($class eq "IV" or $class eq "PVIV") { 
AT>                 return $sv->int_value; 
AT>         } 
AT>         if ($class eq "NV" or $class eq "PVNV") { 
AT>                 return $sv->NV; 
AT>         } 
AT>         if ($class eq "PVMG") { 
AT>                 for (my $mg = $sv->MAGIC; $mg; $mg = $mg->MOREMAGIC) {
AT>                         next if $mg->TYPE ne "V"; 
AT>                         my @v = $mg->PTR =~ /(\d+)/g; 
AT>                         return $v[0] + $v[1] / 1000 + $v[2] / 1000/1000; 
AT>                 } 
AT>         } 
AT>         if ($sv->can("PV")) { 
AT>                 my $v = $sv->PV; 
AT>                 if ($v =~ /^\s*\.?\d/) { 
AT>                         $v =~ s/_//g; 
AT>                         return $v + 0; 
AT>                 } 
AT>         } 
AT>         return undef; 
AT> } 

I think the real problem with sv_version is that its logic is wrong:
it's trying to switch on the representation type of the SV, rather
than the contents. Just because an SV is a PVMG doesn't mean it has
magic now (in particular, because SVs can't be "downgraded").

For a single invocation that shows this problem, try:

my $x = 3;
study $x;
$x = 4;

print sv_version(svref_2object(\$x)), "\n";

The first three lines make $x a PVMG with no string value or V magic
whose value is stored as an integer, but your code skips the branch
that would have called $sv->int_value because it's deciding based on
the SV's class.

I'd suggest testing in the following order:

if (SV has "V" magic) {
   parse V-string
if (SV is IOK) {
   use int_value
if (SV is NOK) {
   use NV
if (SV is POK) {
   parse PV

Hope this helps,

 -- Stephen

Thread Previous Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About