Skip Menu |

This queue is for tickets about the Data-Structure-Util CPAN distribution.

Report information
The Basics
Id: 4324
Status: resolved
Priority: 0/
Queue: Data-Structure-Util

People
Owner: Nobody in particular
Requestors: autarch [...] urth.org
Cc:
AdminCc:

Bug Information
Severity: Normal
Broken in: 0.04
Fixed in: (no value)



Subject: has_circular_ref is fooled by combination of weak and strong references
The has_circular_ref() function can be fooled by the following combination of weak and strong references: my $obj = { key1 => {} }; $obj->{key2}{key3} = $obj->{key1}; # strong ref weaken $obj->{key1}; Apparently it sees the weak reference of key1 first, and never catches the strong references nested more deeply in the data structure. I'm not sure how to work around this. I've attached a version of 02circular.t which includes a test for this, and also cleans up testing for the existence of Scalar::Util::weaken(). On a semi-related note, this function would be much more useful if it could indicate exactly where the circular refs occur, as oppoosed to simply saying they exist.
#!/usr/bin/perl use strict; use warnings; use blib; use Data::Structure::Util qw(has_utf8 utf8_off utf8_on unbless get_blessed has_circular_ref); use Data::Dumper; our $WEAKEN; BEGIN { eval q{ use Scalar::Util qw(weaken isweak) }; if ($@) { eval q{ use Test::Simple tests => 13 }; } else { eval q{ use Test::Simple tests => 17 }; $WEAKEN = 1 if defined &Scalar::Util::weaken; } } ok(1,"we loaded fine..."); my $obj = bless { key1 => [1, 2, 3, bless {} => 'Tagada'], key2 => undef, key3 => { key31 => {}, key32 => bless { bla => [] } => 'Tagada', }, key5 => bless [] => 'Ponie', } => 'Scoobidoo'; $obj->{key4} = \$obj; $obj->{key3}->{key33} = $obj->{key3}->{key31}; my $thing = { var1 => {} }; $thing->{var2} = [ $thing->{var1}->{hello } ]; $thing->{var1}->{hello} = $thing->{var2}; my $obj2 = { key1 => [ sub { [] } ] }; $obj2->{key2} = $obj2->{key1}; my $obj3; $obj3 = \$obj3; my $obj4 = { key1 => $obj3 }; our @V1 = (1, 2, sub {} ); my $obj5 = { key1 => undef, key2 => sub {}, key3 => \@V1, key4 => $obj2, key5 => { key51 => sub {}, key52 => \*STDERR, key53 => [0, \"hello"], }, }; $obj5->{key5}->{key53}->[2] = $obj5->{key5}; $obj5->{key5}->{key54} = $obj5->{key5}->{key53}->[2]; $obj5->{key6} = $obj5->{key5}->{key53}->[2]; $obj5->{key5}->{key55} = $obj5->{key5}->{key53}->[2]; my $obj6 = { key1 => undef }; $obj = $obj6; my $V2 = [1, undef, \5, sub {} ]; foreach (1 .. 50) { $obj->{key2} = {}; $obj->{key1} = $V2; $obj = $obj->{key2}; } $obj->{key3} = \$obj6; ok(! has_circular_ref($thing), "Not a circular ref"); my $ref = has_circular_ref($obj); ok($ref, "Got a circular reference"); ok($ref == $obj, "reference is correct"); ok(! has_circular_ref($obj2), "No circular reference"); ok(has_circular_ref($obj3), "Got a circular reference"); ok(has_circular_ref($obj4), "Got a circular reference"); ok(has_circular_ref($obj5), "Got a circular reference"); ok(has_circular_ref($obj6), "Got a circular reference"); ok($obj6 == has_circular_ref($obj6), "Match reference"); ok(! has_circular_ref(), "No circular reference"); ok(! has_circular_ref( [] ), "No circular reference"); ok(has_circular_ref( [ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\$ref ] ), "Has circular reference"); if ($WEAKEN) { my $obj7 = { key1 => {} }; $obj7->{key1}->{key11} = $obj7->{key1}; ok(has_circular_ref($obj7), "Got a circular reference"); weaken($obj7->{key1}->{key11}); ok(isweak($obj7->{key1}->{key11}), "has weaken reference"); ok(! has_circular_ref($obj7), "No more circular reference"); my $obj8 = { key1 => {} }; $obj8->{key2}{key3} = $obj8->{key1}; weaken $obj8->{key1}; ok(has_circular_ref($obj8)); } else { warn "Scalar::Util XS version not installed, some tests skipped\n"; }
Thanks for the feedback. This a dump of the structure you suggest: $VAR1 = { 'key2' => { 'key3' => {} }, 'key1' => $VAR1->{'key2'}{'key3'} }; I don't see any circular reference in it. has_circular_ref() does return a reference to an element of the circular reference. So you can dump it and see what data it concerns. Ususally, this is enough to guess where the circular ref lies. Next version, I will also add a method circular_off() which attempts to weaken any reference cause of a circular reference. The result should be a structure without any strong circular reference. Have I replied properly?