1#!./perl 2# 3# Copyright (c) 1995-2000, Raphael Manfredi 4# 5# You may redistribute only under the same terms as Perl 5, as specified 6# in the README file that comes with the distribution. 7# 8use Config; 9 10sub BEGIN { 11 unshift @INC, 't'; 12 unshift @INC, 't/compat' if $] < 5.006002; 13 if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { 14 print "1..0 # Skip: Storable was not built\n"; 15 exit 0; 16 } 17} 18 19use Storable qw(freeze thaw dclone); 20 21$Storable::flags = Storable::FLAGS_COMPAT; 22 23use Test::More tests => 39; 24 25package OBJ_REAL; 26 27use Storable qw(freeze thaw); 28 29@x = ('a', 1); 30 31sub make { bless [], shift } 32 33sub STORABLE_freeze { 34 my $self = shift; 35 my $cloning = shift; 36 die "STORABLE_freeze" unless Storable::is_storing; 37 return (freeze(\@x), $self); 38} 39 40sub STORABLE_thaw { 41 my $self = shift; 42 my $cloning = shift; 43 my ($x, $obj) = @_; 44 die "STORABLE_thaw #1" unless $obj eq $self; 45 my $len = length $x; 46 my $a = thaw $x; 47 die "STORABLE_thaw #2" unless ref $a eq 'ARRAY'; 48 die "STORABLE_thaw #3" unless @$a == 2 && $a->[0] eq 'a' && $a->[1] == 1; 49 @$self = @$a; 50 die "STORABLE_thaw #4" unless Storable::is_retrieving; 51} 52 53package OBJ_SYNC; 54 55@x = ('a', 1); 56 57sub make { bless {}, shift } 58 59sub STORABLE_freeze { 60 my $self = shift; 61 my ($cloning) = @_; 62 return if $cloning; 63 return ("", \@x, $self); 64} 65 66sub STORABLE_thaw { 67 my $self = shift; 68 my ($cloning, $undef, $a, $obj) = @_; 69 die "STORABLE_thaw #1" unless $obj eq $self; 70 die "STORABLE_thaw #2" unless ref $a eq 'ARRAY' || @$a != 2; 71 $self->{ok} = $self; 72} 73 74package OBJ_SYNC2; 75 76use Storable qw(dclone); 77 78sub make { 79 my $self = bless {}, shift; 80 my ($ext) = @_; 81 $self->{sync} = OBJ_SYNC->make; 82 $self->{ext} = $ext; 83 return $self; 84} 85 86sub STORABLE_freeze { 87 my $self = shift; 88 my %copy = %$self; 89 my $r = \%copy; 90 my $t = dclone($r->{sync}); 91 return ("", [$t, $self->{ext}], $r, $self, $r->{ext}); 92} 93 94sub STORABLE_thaw { 95 my $self = shift; 96 my ($cloning, $undef, $a, $r, $obj, $ext) = @_; 97 die "STORABLE_thaw #1" unless $obj eq $self; 98 die "STORABLE_thaw #2" unless ref $a eq 'ARRAY'; 99 die "STORABLE_thaw #3" unless ref $r eq 'HASH'; 100 die "STORABLE_thaw #4" unless $a->[1] == $r->{ext}; 101 $self->{ok} = $self; 102 ($self->{sync}, $self->{ext}) = @$a; 103} 104 105package OBJ_REAL2; 106 107use Storable qw(freeze thaw); 108 109$MAX = 20; 110$recursed = 0; 111$hook_called = 0; 112 113sub make { bless [], shift } 114 115sub STORABLE_freeze { 116 my $self = shift; 117 $hook_called++; 118 return (freeze($self), $self) if ++$recursed < $MAX; 119 return ("no", $self); 120} 121 122sub STORABLE_thaw { 123 my $self = shift; 124 my $cloning = shift; 125 my ($x, $obj) = @_; 126 die "STORABLE_thaw #1" unless $obj eq $self; 127 $self->[0] = thaw($x) if $x ne "no"; 128 $recursed--; 129} 130 131package main; 132 133my $real = OBJ_REAL->make; 134my $x = freeze $real; 135isnt($x, undef); 136 137my $y = thaw $x; 138is(ref $y, 'OBJ_REAL'); 139is($y->[0], 'a'); 140is($y->[1], 1); 141 142my $sync = OBJ_SYNC->make; 143$x = freeze $sync; 144isnt($x, undef); 145 146$y = thaw $x; 147is(ref $y, 'OBJ_SYNC'); 148is($y->{ok}, $y); 149 150my $ext = [1, 2]; 151$sync = OBJ_SYNC2->make($ext); 152$x = freeze [$sync, $ext]; 153isnt($x, undef); 154 155my $z = thaw $x; 156$y = $z->[0]; 157is(ref $y, 'OBJ_SYNC2'); 158is($y->{ok}, $y); 159is(ref $y->{sync}, 'OBJ_SYNC'); 160is($y->{ext}, $z->[1]); 161 162$real = OBJ_REAL2->make; 163$x = freeze $real; 164isnt($x, undef); 165is($OBJ_REAL2::recursed, $OBJ_REAL2::MAX); 166is($OBJ_REAL2::hook_called, $OBJ_REAL2::MAX); 167 168$y = thaw $x; 169is(ref $y, 'OBJ_REAL2'); 170is($OBJ_REAL2::recursed, 0); 171 172$x = dclone $real; 173isnt($x, undef); 174is(ref $x, 'OBJ_REAL2'); 175is($OBJ_REAL2::recursed, 0); 176is($OBJ_REAL2::hook_called, 2 * $OBJ_REAL2::MAX); 177 178is(Storable::is_storing, ''); 179is(Storable::is_retrieving, ''); 180 181# 182# The following was a test-case that Salvador Ortiz Garcia <sog@msg.com.mx> 183# sent me, along with a proposed fix. 184# 185 186package Foo; 187 188sub new { 189 my $class = shift; 190 my $dat = shift; 191 return bless {dat => $dat}, $class; 192} 193 194package Bar; 195sub new { 196 my $class = shift; 197 return bless { 198 a => 'dummy', 199 b => [ 200 Foo->new(1), 201 Foo->new(2), # Second instance of a Foo 202 ] 203 }, $class; 204} 205 206sub STORABLE_freeze { 207 my($self,$clonning) = @_; 208 return "$self->{a}", $self->{b}; 209} 210 211sub STORABLE_thaw { 212 my($self,$clonning,$dummy,$o) = @_; 213 $self->{a} = $dummy; 214 $self->{b} = $o; 215} 216 217package main; 218 219my $bar = new Bar; 220my $bar2 = thaw freeze $bar; 221 222is(ref($bar2), 'Bar'); 223is(ref($bar->{b}[0]), 'Foo'); 224is(ref($bar->{b}[1]), 'Foo'); 225is(ref($bar2->{b}[0]), 'Foo'); 226is(ref($bar2->{b}[1]), 'Foo'); 227 228# 229# The following attempts to make sure blessed objects are blessed ASAP 230# at retrieve time. 231# 232 233package CLASS_1; 234 235sub make { 236 my $self = bless {}, shift; 237 return $self; 238} 239 240package CLASS_2; 241 242sub make { 243 my $self = bless {}, shift; 244 my ($o) = @_; 245 $self->{c1} = CLASS_1->make(); 246 $self->{o} = $o; 247 $self->{c3} = bless CLASS_1->make(), "CLASS_3"; 248 $o->set_c2($self); 249 return $self; 250} 251 252sub STORABLE_freeze { 253 my($self, $clonning) = @_; 254 return "", $self->{c1}, $self->{c3}, $self->{o}; 255} 256 257sub STORABLE_thaw { 258 my($self, $clonning, $frozen, $c1, $c3, $o) = @_; 259 main::is(ref $self, "CLASS_2"); 260 main::is(ref $c1, "CLASS_1"); 261 main::is(ref $c3, "CLASS_3"); 262 main::is(ref $o, "CLASS_OTHER"); 263 $self->{c1} = $c1; 264 $self->{c3} = $c3; 265} 266 267package CLASS_OTHER; 268 269sub make { 270 my $self = bless {}, shift; 271 return $self; 272} 273 274sub set_c2 { $_[0]->{c2} = $_[1] } 275 276# 277# Is the reference count of the extra references returned from a 278# STORABLE_freeze hook correct? [ID 20020601.005 (#9436)] 279# 280package Foo2; 281 282sub new { 283 my $self = bless {}, $_[0]; 284 $self->{freezed} = "$self"; 285 return $self; 286} 287 288sub DESTROY { 289 my $self = shift; 290 $::refcount_ok = 1 unless "$self" eq $self->{freezed}; 291} 292 293package Foo3; 294 295sub new { 296 bless {}, $_[0]; 297} 298 299sub STORABLE_freeze { 300 my $obj = shift; 301 return ("", $obj, Foo2->new); 302} 303 304sub STORABLE_thaw { } # Not really used 305 306package main; 307 308my $o = CLASS_OTHER->make(); 309my $c2 = CLASS_2->make($o); 310my $so = thaw freeze $o; 311 312our $refcount_ok = 0; 313thaw freeze(Foo3->new); 314is($refcount_ok, 1, "check refcount"); 315 316# Check stack overflows [cpan #97526] 317# JSON::XS limits this to 512. 318# Small 64bit systems fail with 1200 (c++ debugging), with gcc 3000. 319# Optimized 64bit allows up to 33.000 recursion depth. 320# with asan the limit is 255 though. 321 322local $Storable::recursion_limit = 30; 323local $Storable::recursion_limit_hash = 20; 324sub MAX_DEPTH () { Storable::stack_depth() } 325sub MAX_DEPTH_HASH () { Storable::stack_depth_hash() } 326{ 327 my $t; 328 print "# max depth ", MAX_DEPTH, "\n"; 329 $t = [$t] for 1 .. MAX_DEPTH; 330 dclone $t; 331 pass "can nest ".MAX_DEPTH." array refs"; 332} 333{ 334 my $t; 335 $t = {1=>$t} for 1 .. MAX_DEPTH_HASH-10; 336 dclone $t; 337 pass "can nest ".(MAX_DEPTH_HASH)." hash refs"; 338} 339{ 340 my (@t); 341 push @t, [{}] for 1..5000; 342 #diag 'trying simple array[5000] stack overflow, no recursion'; 343 dclone \@t; 344 is $@, '', 'No simple array[5000] stack overflow #257'; 345} 346 347eval { 348 my $t; 349 $t = [$t] for 1 .. MAX_DEPTH*2; 350 eval { note('trying catching recursive aref stack overflow') }; 351 dclone $t; 352}; 353like $@, qr/Max\. recursion depth with nested structures exceeded/, 354 'Caught aref stack overflow '.MAX_DEPTH*2; 355 356if ($ENV{APPVEYOR} and length(pack "p", "") >= 8) { 357 # TODO: need to repro this fail on a small machine. 358 ok(1, "skip dclone of big hash"); 359} 360else { 361 eval { 362 my $t; 363 # 35.000 will cause appveyor 64bit windows to fail earlier 364 $t = {1=>$t} for 1 .. MAX_DEPTH * 2; 365 eval { note('trying catching recursive href stack overflow') }; 366 dclone $t; 367 }; 368 like $@, qr/Max\. recursion depth with nested structures exceeded/, 369 'Caught href stack overflow '.MAX_DEPTH_HASH*2; 370} 371 372{ 373 # perl #133326 374 my @tt; 375 #$Storable::DEBUGME=1; 376 for (1..16000) { 377 my $t = [[[]]]; 378 push @tt, $t; 379 } 380 ok(eval { dclone \@tt; 1 }, 381 "low depth structure shouldn't be treated as nested"); 382} 383