1b39c5158Smillert#!./perl 2b39c5158Smillert# 3b39c5158Smillert# Copyright (c) 1995-2000, Raphael Manfredi 4b39c5158Smillert# 5b39c5158Smillert# You may redistribute only under the same terms as Perl 5, as specified 6b39c5158Smillert# in the README file that comes with the distribution. 7b39c5158Smillert# 85759b3d2Safresh1use Config; 9b39c5158Smillert 10b39c5158Smillertsub BEGIN { 11b39c5158Smillert unshift @INC, 't'; 12898184e3Ssthen unshift @INC, 't/compat' if $] < 5.006002; 13b39c5158Smillert if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { 14b39c5158Smillert print "1..0 # Skip: Storable was not built\n"; 15b39c5158Smillert exit 0; 16b39c5158Smillert } 17b39c5158Smillert} 18b39c5158Smillert 19b39c5158Smillertuse Storable qw(freeze thaw dclone); 205759b3d2Safresh1 215759b3d2Safresh1$Storable::flags = Storable::FLAGS_COMPAT; 225759b3d2Safresh1 23e603c72fSafresh1use Test::More tests => 39; 24b39c5158Smillert 25b39c5158Smillertpackage OBJ_REAL; 26b39c5158Smillert 27b39c5158Smillertuse Storable qw(freeze thaw); 28b39c5158Smillert 29b39c5158Smillert@x = ('a', 1); 30b39c5158Smillert 31b39c5158Smillertsub make { bless [], shift } 32b39c5158Smillert 33b39c5158Smillertsub STORABLE_freeze { 34b39c5158Smillert my $self = shift; 35b39c5158Smillert my $cloning = shift; 36b39c5158Smillert die "STORABLE_freeze" unless Storable::is_storing; 37b39c5158Smillert return (freeze(\@x), $self); 38b39c5158Smillert} 39b39c5158Smillert 40b39c5158Smillertsub STORABLE_thaw { 41b39c5158Smillert my $self = shift; 42b39c5158Smillert my $cloning = shift; 43b39c5158Smillert my ($x, $obj) = @_; 44b39c5158Smillert die "STORABLE_thaw #1" unless $obj eq $self; 45b39c5158Smillert my $len = length $x; 46b39c5158Smillert my $a = thaw $x; 47b39c5158Smillert die "STORABLE_thaw #2" unless ref $a eq 'ARRAY'; 48b39c5158Smillert die "STORABLE_thaw #3" unless @$a == 2 && $a->[0] eq 'a' && $a->[1] == 1; 49b39c5158Smillert @$self = @$a; 50b39c5158Smillert die "STORABLE_thaw #4" unless Storable::is_retrieving; 51b39c5158Smillert} 52b39c5158Smillert 53b39c5158Smillertpackage OBJ_SYNC; 54b39c5158Smillert 55b39c5158Smillert@x = ('a', 1); 56b39c5158Smillert 57b39c5158Smillertsub make { bless {}, shift } 58b39c5158Smillert 59b39c5158Smillertsub STORABLE_freeze { 60b39c5158Smillert my $self = shift; 61b39c5158Smillert my ($cloning) = @_; 62b39c5158Smillert return if $cloning; 63b39c5158Smillert return ("", \@x, $self); 64b39c5158Smillert} 65b39c5158Smillert 66b39c5158Smillertsub STORABLE_thaw { 67b39c5158Smillert my $self = shift; 68b39c5158Smillert my ($cloning, $undef, $a, $obj) = @_; 69b39c5158Smillert die "STORABLE_thaw #1" unless $obj eq $self; 70b39c5158Smillert die "STORABLE_thaw #2" unless ref $a eq 'ARRAY' || @$a != 2; 71b39c5158Smillert $self->{ok} = $self; 72b39c5158Smillert} 73b39c5158Smillert 74b39c5158Smillertpackage OBJ_SYNC2; 75b39c5158Smillert 76b39c5158Smillertuse Storable qw(dclone); 77b39c5158Smillert 78b39c5158Smillertsub make { 79b39c5158Smillert my $self = bless {}, shift; 80b39c5158Smillert my ($ext) = @_; 81b39c5158Smillert $self->{sync} = OBJ_SYNC->make; 82b39c5158Smillert $self->{ext} = $ext; 83b39c5158Smillert return $self; 84b39c5158Smillert} 85b39c5158Smillert 86b39c5158Smillertsub STORABLE_freeze { 87b39c5158Smillert my $self = shift; 88b39c5158Smillert my %copy = %$self; 89b39c5158Smillert my $r = \%copy; 90b39c5158Smillert my $t = dclone($r->{sync}); 91b39c5158Smillert return ("", [$t, $self->{ext}], $r, $self, $r->{ext}); 92b39c5158Smillert} 93b39c5158Smillert 94b39c5158Smillertsub STORABLE_thaw { 95b39c5158Smillert my $self = shift; 96b39c5158Smillert my ($cloning, $undef, $a, $r, $obj, $ext) = @_; 97b39c5158Smillert die "STORABLE_thaw #1" unless $obj eq $self; 98b39c5158Smillert die "STORABLE_thaw #2" unless ref $a eq 'ARRAY'; 99b39c5158Smillert die "STORABLE_thaw #3" unless ref $r eq 'HASH'; 100b39c5158Smillert die "STORABLE_thaw #4" unless $a->[1] == $r->{ext}; 101b39c5158Smillert $self->{ok} = $self; 102b39c5158Smillert ($self->{sync}, $self->{ext}) = @$a; 103b39c5158Smillert} 104b39c5158Smillert 105b39c5158Smillertpackage OBJ_REAL2; 106b39c5158Smillert 107b39c5158Smillertuse Storable qw(freeze thaw); 108b39c5158Smillert 109b39c5158Smillert$MAX = 20; 110b39c5158Smillert$recursed = 0; 111b39c5158Smillert$hook_called = 0; 112b39c5158Smillert 113b39c5158Smillertsub make { bless [], shift } 114b39c5158Smillert 115b39c5158Smillertsub STORABLE_freeze { 116b39c5158Smillert my $self = shift; 117b39c5158Smillert $hook_called++; 118b39c5158Smillert return (freeze($self), $self) if ++$recursed < $MAX; 119b39c5158Smillert return ("no", $self); 120b39c5158Smillert} 121b39c5158Smillert 122b39c5158Smillertsub STORABLE_thaw { 123b39c5158Smillert my $self = shift; 124b39c5158Smillert my $cloning = shift; 125b39c5158Smillert my ($x, $obj) = @_; 126b39c5158Smillert die "STORABLE_thaw #1" unless $obj eq $self; 127b39c5158Smillert $self->[0] = thaw($x) if $x ne "no"; 128b39c5158Smillert $recursed--; 129b39c5158Smillert} 130b39c5158Smillert 131b39c5158Smillertpackage main; 132b39c5158Smillert 133b39c5158Smillertmy $real = OBJ_REAL->make; 134b39c5158Smillertmy $x = freeze $real; 135898184e3Ssthenisnt($x, undef); 136b39c5158Smillert 137b39c5158Smillertmy $y = thaw $x; 138898184e3Ssthenis(ref $y, 'OBJ_REAL'); 139898184e3Ssthenis($y->[0], 'a'); 140898184e3Ssthenis($y->[1], 1); 141b39c5158Smillert 142b39c5158Smillertmy $sync = OBJ_SYNC->make; 143b39c5158Smillert$x = freeze $sync; 144898184e3Ssthenisnt($x, undef); 145b39c5158Smillert 146b39c5158Smillert$y = thaw $x; 147898184e3Ssthenis(ref $y, 'OBJ_SYNC'); 148898184e3Ssthenis($y->{ok}, $y); 149b39c5158Smillert 150b39c5158Smillertmy $ext = [1, 2]; 151b39c5158Smillert$sync = OBJ_SYNC2->make($ext); 152b39c5158Smillert$x = freeze [$sync, $ext]; 153898184e3Ssthenisnt($x, undef); 154b39c5158Smillert 155b39c5158Smillertmy $z = thaw $x; 156b39c5158Smillert$y = $z->[0]; 157898184e3Ssthenis(ref $y, 'OBJ_SYNC2'); 158898184e3Ssthenis($y->{ok}, $y); 159898184e3Ssthenis(ref $y->{sync}, 'OBJ_SYNC'); 160898184e3Ssthenis($y->{ext}, $z->[1]); 161b39c5158Smillert 162b39c5158Smillert$real = OBJ_REAL2->make; 163b39c5158Smillert$x = freeze $real; 164898184e3Ssthenisnt($x, undef); 165898184e3Ssthenis($OBJ_REAL2::recursed, $OBJ_REAL2::MAX); 166898184e3Ssthenis($OBJ_REAL2::hook_called, $OBJ_REAL2::MAX); 167b39c5158Smillert 168b39c5158Smillert$y = thaw $x; 169898184e3Ssthenis(ref $y, 'OBJ_REAL2'); 170898184e3Ssthenis($OBJ_REAL2::recursed, 0); 171b39c5158Smillert 172b39c5158Smillert$x = dclone $real; 173898184e3Ssthenisnt($x, undef); 174898184e3Ssthenis(ref $x, 'OBJ_REAL2'); 175898184e3Ssthenis($OBJ_REAL2::recursed, 0); 176898184e3Ssthenis($OBJ_REAL2::hook_called, 2 * $OBJ_REAL2::MAX); 177b39c5158Smillert 178898184e3Ssthenis(Storable::is_storing, ''); 179898184e3Ssthenis(Storable::is_retrieving, ''); 180b39c5158Smillert 181b39c5158Smillert# 182b39c5158Smillert# The following was a test-case that Salvador Ortiz Garcia <sog@msg.com.mx> 183b39c5158Smillert# sent me, along with a proposed fix. 184b39c5158Smillert# 185b39c5158Smillert 186b39c5158Smillertpackage Foo; 187b39c5158Smillert 188b39c5158Smillertsub new { 189b39c5158Smillert my $class = shift; 190b39c5158Smillert my $dat = shift; 191b39c5158Smillert return bless {dat => $dat}, $class; 192b39c5158Smillert} 193b39c5158Smillert 194b39c5158Smillertpackage Bar; 195b39c5158Smillertsub new { 196b39c5158Smillert my $class = shift; 197b39c5158Smillert return bless { 198b39c5158Smillert a => 'dummy', 199b39c5158Smillert b => [ 200b39c5158Smillert Foo->new(1), 201b39c5158Smillert Foo->new(2), # Second instance of a Foo 202b39c5158Smillert ] 203b39c5158Smillert }, $class; 204b39c5158Smillert} 205b39c5158Smillert 206b39c5158Smillertsub STORABLE_freeze { 207b39c5158Smillert my($self,$clonning) = @_; 208b39c5158Smillert return "$self->{a}", $self->{b}; 209b39c5158Smillert} 210b39c5158Smillert 211b39c5158Smillertsub STORABLE_thaw { 212b39c5158Smillert my($self,$clonning,$dummy,$o) = @_; 213b39c5158Smillert $self->{a} = $dummy; 214b39c5158Smillert $self->{b} = $o; 215b39c5158Smillert} 216b39c5158Smillert 217b39c5158Smillertpackage main; 218b39c5158Smillert 219b39c5158Smillertmy $bar = new Bar; 220b39c5158Smillertmy $bar2 = thaw freeze $bar; 221b39c5158Smillert 222898184e3Ssthenis(ref($bar2), 'Bar'); 223898184e3Ssthenis(ref($bar->{b}[0]), 'Foo'); 224898184e3Ssthenis(ref($bar->{b}[1]), 'Foo'); 225898184e3Ssthenis(ref($bar2->{b}[0]), 'Foo'); 226898184e3Ssthenis(ref($bar2->{b}[1]), 'Foo'); 227b39c5158Smillert 228b39c5158Smillert# 229b39c5158Smillert# The following attempts to make sure blessed objects are blessed ASAP 230b39c5158Smillert# at retrieve time. 231b39c5158Smillert# 232b39c5158Smillert 233b39c5158Smillertpackage CLASS_1; 234b39c5158Smillert 235b39c5158Smillertsub make { 236b39c5158Smillert my $self = bless {}, shift; 237b39c5158Smillert return $self; 238b39c5158Smillert} 239b39c5158Smillert 240b39c5158Smillertpackage CLASS_2; 241b39c5158Smillert 242b39c5158Smillertsub make { 243b39c5158Smillert my $self = bless {}, shift; 244b39c5158Smillert my ($o) = @_; 245b39c5158Smillert $self->{c1} = CLASS_1->make(); 246b39c5158Smillert $self->{o} = $o; 247b39c5158Smillert $self->{c3} = bless CLASS_1->make(), "CLASS_3"; 248b39c5158Smillert $o->set_c2($self); 249b39c5158Smillert return $self; 250b39c5158Smillert} 251b39c5158Smillert 252b39c5158Smillertsub STORABLE_freeze { 253b39c5158Smillert my($self, $clonning) = @_; 254b39c5158Smillert return "", $self->{c1}, $self->{c3}, $self->{o}; 255b39c5158Smillert} 256b39c5158Smillert 257b39c5158Smillertsub STORABLE_thaw { 258b39c5158Smillert my($self, $clonning, $frozen, $c1, $c3, $o) = @_; 259898184e3Ssthen main::is(ref $self, "CLASS_2"); 260898184e3Ssthen main::is(ref $c1, "CLASS_1"); 261898184e3Ssthen main::is(ref $c3, "CLASS_3"); 262898184e3Ssthen main::is(ref $o, "CLASS_OTHER"); 263b39c5158Smillert $self->{c1} = $c1; 264b39c5158Smillert $self->{c3} = $c3; 265b39c5158Smillert} 266b39c5158Smillert 267b39c5158Smillertpackage CLASS_OTHER; 268b39c5158Smillert 269b39c5158Smillertsub make { 270b39c5158Smillert my $self = bless {}, shift; 271b39c5158Smillert return $self; 272b39c5158Smillert} 273b39c5158Smillert 274b39c5158Smillertsub set_c2 { $_[0]->{c2} = $_[1] } 275b39c5158Smillert 276b39c5158Smillert# 277b39c5158Smillert# Is the reference count of the extra references returned from a 2785759b3d2Safresh1# STORABLE_freeze hook correct? [ID 20020601.005 (#9436)] 279b39c5158Smillert# 280b39c5158Smillertpackage Foo2; 281b39c5158Smillert 282b39c5158Smillertsub new { 283b39c5158Smillert my $self = bless {}, $_[0]; 284b39c5158Smillert $self->{freezed} = "$self"; 285b39c5158Smillert return $self; 286b39c5158Smillert} 287b39c5158Smillert 288b39c5158Smillertsub DESTROY { 289b39c5158Smillert my $self = shift; 290b39c5158Smillert $::refcount_ok = 1 unless "$self" eq $self->{freezed}; 291b39c5158Smillert} 292b39c5158Smillert 293b39c5158Smillertpackage Foo3; 294b39c5158Smillert 295b39c5158Smillertsub new { 296b39c5158Smillert bless {}, $_[0]; 297b39c5158Smillert} 298b39c5158Smillert 299b39c5158Smillertsub STORABLE_freeze { 300b39c5158Smillert my $obj = shift; 301b39c5158Smillert return ("", $obj, Foo2->new); 302b39c5158Smillert} 303b39c5158Smillert 304b39c5158Smillertsub STORABLE_thaw { } # Not really used 305b39c5158Smillert 306b39c5158Smillertpackage main; 307b39c5158Smillert 308b39c5158Smillertmy $o = CLASS_OTHER->make(); 309b39c5158Smillertmy $c2 = CLASS_2->make($o); 310b39c5158Smillertmy $so = thaw freeze $o; 311b39c5158Smillert 3125759b3d2Safresh1our $refcount_ok = 0; 313b39c5158Smillertthaw freeze(Foo3->new); 3145759b3d2Safresh1is($refcount_ok, 1, "check refcount"); 3155759b3d2Safresh1 3165759b3d2Safresh1# Check stack overflows [cpan #97526] 3175759b3d2Safresh1# JSON::XS limits this to 512. 3185759b3d2Safresh1# Small 64bit systems fail with 1200 (c++ debugging), with gcc 3000. 3195759b3d2Safresh1# Optimized 64bit allows up to 33.000 recursion depth. 3205759b3d2Safresh1# with asan the limit is 255 though. 321b46d8ef2Safresh1 322b46d8ef2Safresh1local $Storable::recursion_limit = 30; 323b46d8ef2Safresh1local $Storable::recursion_limit_hash = 20; 3245759b3d2Safresh1sub MAX_DEPTH () { Storable::stack_depth() } 3255759b3d2Safresh1sub MAX_DEPTH_HASH () { Storable::stack_depth_hash() } 3265759b3d2Safresh1{ 3275759b3d2Safresh1 my $t; 3285759b3d2Safresh1 print "# max depth ", MAX_DEPTH, "\n"; 3295759b3d2Safresh1 $t = [$t] for 1 .. MAX_DEPTH; 3305759b3d2Safresh1 dclone $t; 3315759b3d2Safresh1 pass "can nest ".MAX_DEPTH." array refs"; 3325759b3d2Safresh1} 3335759b3d2Safresh1{ 3345759b3d2Safresh1 my $t; 3355759b3d2Safresh1 $t = {1=>$t} for 1 .. MAX_DEPTH_HASH-10; 3365759b3d2Safresh1 dclone $t; 3375759b3d2Safresh1 pass "can nest ".(MAX_DEPTH_HASH)." hash refs"; 3385759b3d2Safresh1} 3395759b3d2Safresh1{ 3405759b3d2Safresh1 my (@t); 3415759b3d2Safresh1 push @t, [{}] for 1..5000; 3425759b3d2Safresh1 #diag 'trying simple array[5000] stack overflow, no recursion'; 3435759b3d2Safresh1 dclone \@t; 3445759b3d2Safresh1 is $@, '', 'No simple array[5000] stack overflow #257'; 3455759b3d2Safresh1} 3465759b3d2Safresh1 3475759b3d2Safresh1eval { 3485759b3d2Safresh1 my $t; 3495759b3d2Safresh1 $t = [$t] for 1 .. MAX_DEPTH*2; 350*56d68f1eSafresh1 eval { note('trying catching recursive aref stack overflow') }; 3515759b3d2Safresh1 dclone $t; 3525759b3d2Safresh1}; 3535759b3d2Safresh1like $@, qr/Max\. recursion depth with nested structures exceeded/, 3545759b3d2Safresh1 'Caught aref stack overflow '.MAX_DEPTH*2; 3555759b3d2Safresh1 3565759b3d2Safresh1if ($ENV{APPVEYOR} and length(pack "p", "") >= 8) { 3575759b3d2Safresh1 # TODO: need to repro this fail on a small machine. 3585759b3d2Safresh1 ok(1, "skip dclone of big hash"); 3595759b3d2Safresh1} 3605759b3d2Safresh1else { 3615759b3d2Safresh1 eval { 3625759b3d2Safresh1 my $t; 3635759b3d2Safresh1 # 35.000 will cause appveyor 64bit windows to fail earlier 3645759b3d2Safresh1 $t = {1=>$t} for 1 .. MAX_DEPTH * 2; 365*56d68f1eSafresh1 eval { note('trying catching recursive href stack overflow') }; 3665759b3d2Safresh1 dclone $t; 3675759b3d2Safresh1 }; 3685759b3d2Safresh1 like $@, qr/Max\. recursion depth with nested structures exceeded/, 369e603c72fSafresh1 'Caught href stack overflow '.MAX_DEPTH_HASH*2; 370e603c72fSafresh1} 371e603c72fSafresh1 372e603c72fSafresh1{ 373e603c72fSafresh1 # perl #133326 374e603c72fSafresh1 my @tt; 375e603c72fSafresh1 #$Storable::DEBUGME=1; 376e603c72fSafresh1 for (1..16000) { 377e603c72fSafresh1 my $t = [[[]]]; 378e603c72fSafresh1 push @tt, $t; 379e603c72fSafresh1 } 380e603c72fSafresh1 ok(eval { dclone \@tt; 1 }, 381e603c72fSafresh1 "low depth structure shouldn't be treated as nested"); 3825759b3d2Safresh1} 383