1#!./perl 2 3BEGIN { 4 unless (-d 'blib') { 5 chdir 't' if -d 't'; 6 } 7 require q(./test.pl); 8 set_up_inc('../lib'); 9} 10 11use strict; 12use warnings; 13plan(tests => 54); 14 15{ 16 package New; 17 use strict; 18 use warnings; 19 20 package Old; 21 use strict; 22 use warnings; 23 24 { 25 no strict 'refs'; 26 *{'Old::'} = *{'New::'}; 27 } 28} 29 30ok (Old->isa (New::), 'Old inherits from New'); 31ok (New->isa (Old::), 'New inherits from Old'); 32 33object_ok (bless ({}, Old::), New::, 'Old object'); 34object_ok (bless ({}, New::), Old::, 'New object'); 35 36 37# Test that replacing a package by assigning to an existing glob 38# invalidates the isa caches 39for( 40 { 41 name => 'assigning a glob to a glob', 42 code => '$life_raft = $::{"Left::"}; *Left:: = $::{"Right::"}', 43 }, 44 { 45 name => 'assigning a string to a glob', 46 code => '$life_raft = $::{"Left::"}; *Left:: = "Right::"', 47 }, 48 { 49 name => 'assigning a stashref to a glob', 50 code => '$life_raft = \%Left::; *Left:: = \%Right::', 51 }, 52) { 53 fresh_perl_is 54 q~ 55 @Subclass::ISA = "Left"; 56 @Left::ISA = "TopLeft"; 57 58 sub TopLeft::speak { "Woof!" } 59 sub TopRight::speak { "Bow-wow!" } 60 61 my $thing = bless [], "Subclass"; 62 63 # mro_package_moved needs to know to skip non-globs 64 $Right::{"gleck::"} = 3; 65 66 @Right::ISA = 'TopRight'; 67 my $life_raft; 68 __code__; 69 70 print $thing->speak, "\n"; 71 72 undef $life_raft; 73 print $thing->speak, "\n"; 74 ~ =~ s\__code__\$$_{code}\r, 75 "Bow-wow!\nBow-wow!\n", 76 {}, 77 "replacing packages by $$_{name} updates isa caches"; 78} 79 80# Similar test, but with nested packages 81# 82# TopLeft (Woof) TopRight (Bow-wow) 83# | | 84# Left::Side <- Right::Side 85# | 86# Subclass 87# 88# This test assigns Right:: to Left::, indirectly making Left::Side an 89# alias to Right::Side (following the arrow in the diagram). 90for( 91 { 92 name => 'assigning a glob to a glob', 93 code => '$life_raft = $::{"Left::"}; *Left:: = $::{"Right::"}', 94 }, 95 { 96 name => 'assigning a string to a glob', 97 code => '$life_raft = $::{"Left::"}; *Left:: = "Right::"', 98 }, 99 { 100 name => 'assigning a stashref to a glob', 101 code => '$life_raft = \%Left::; *Left:: = \%Right::', 102 }, 103) { 104 fresh_perl_is 105 q~ 106 @Subclass::ISA = "Left::Side"; 107 @Left::Side::ISA = "TopLeft"; 108 109 sub TopLeft::speak { "Woof!" } 110 sub TopRight::speak { "Bow-wow!" } 111 112 my $thing = bless [], "Subclass"; 113 114 @Right::Side::ISA = 'TopRight'; 115 my $life_raft; 116 __code__; 117 118 print $thing->speak, "\n"; 119 120 undef $life_raft; 121 print $thing->speak, "\n"; 122 ~ =~ s\__code__\$$_{code}\r, 123 "Bow-wow!\nBow-wow!\n", 124 {}, 125 "replacing nested packages by $$_{name} updates isa caches"; 126} 127 128# Another nested package test, in which the isa cache needs to be reset on 129# the subclass of a package that does not exist. 130# 131# Parenthesized packages do not exist. 132# 133# outer::inner ( clone::inner ) 134# | | 135# left right 136# 137# outer -> clone 138# 139# This test assigns outer:: to clone::, making clone::inner an alias to 140# outer::inner. 141# 142# Then we also run the test again, but without outer::inner 143for( 144 { 145 name => 'assigning a glob to a glob', 146 code => '*clone:: = *outer::', 147 }, 148 { 149 name => 'assigning a string to a glob', 150 code => '*clone:: = "outer::"', 151 }, 152 { 153 name => 'assigning a stashref to a glob', 154 code => '*clone:: = \%outer::', 155 }, 156) { 157 for my $tail ('inner', 'inner::', 'inner:::', 'inner::::') { 158 fresh_perl_is 159 q~ 160 my $tail = shift; 161 @left::ISA = "outer::$tail"; 162 @right::ISA = "clone::$tail"; 163 bless [], "outer::$tail"; # autovivify the stash 164 165 __code__; 166 167 print "ok 1", "\n" if left->isa("clone::$tail"); 168 print "ok 2", "\n" if right->isa("outer::$tail"); 169 print "ok 3", "\n" if right->isa("clone::$tail"); 170 print "ok 4", "\n" if left->isa("outer::$tail"); 171 ~ =~ s\__code__\$$_{code}\r, 172 "ok 1\nok 2\nok 3\nok 4\n", 173 { args => [$tail] }, 174 "replacing nonexistent nested packages by $$_{name} updates isa caches" 175 ." ($tail)"; 176 177 # Same test but with the subpackage autovivified after the assignment 178 fresh_perl_is 179 q~ 180 my $tail = shift; 181 @left::ISA = "outer::$tail"; 182 @right::ISA = "clone::$tail"; 183 184 __code__; 185 186 bless [], "outer::$tail"; 187 188 print "ok 1", "\n" if left->isa("clone::$tail"); 189 print "ok 2", "\n" if right->isa("outer::$tail"); 190 print "ok 3", "\n" if right->isa("clone::$tail"); 191 print "ok 4", "\n" if left->isa("outer::$tail"); 192 ~ =~ s\__code__\$$_{code}\r, 193 "ok 1\nok 2\nok 3\nok 4\n", 194 { args => [$tail] }, 195 "Giving nonexistent packages multiple effective names by $$_{name}" 196 . " ($tail)"; 197 } 198} 199 200no warnings; # temporary; there seems to be a scoping bug, as this does not 201 # work when placed in the blocks below 202 203# Test that deleting stash elements containing 204# subpackages also invalidates the isa cache. 205# Maybe this does not belong in package_aliases.t, but it is closely 206# related to the tests immediately preceding. 207{ 208 @Pet::ISA = ("Cur", "Hound"); 209 @Cur::ISA = "Hylactete"; 210 211 sub Hylactete::speak { "Arff!" } 212 sub Hound::speak { "Woof!" } 213 214 my $pet = bless [], "Pet"; 215 216 my $life_raft = delete $::{'Cur::'}; 217 218 is $pet->speak, 'Woof!', 219 'deleting a stash from its parent stash invalidates the isa caches'; 220 221 undef $life_raft; 222 is $pet->speak, 'Woof!', 223 'the deleted stash is gone completely when freed'; 224} 225# Same thing, but with nested packages 226{ 227 @Pett::ISA = ("Curr::Curr::Curr", "Hownd"); 228 @Curr::Curr::Curr::ISA = "Latrator"; 229 230 sub Latrator::speak { "Arff!" } 231 sub Hownd::speak { "Woof!" } 232 233 my $pet = bless [], "Pett"; 234 235 my $life_raft = delete $::{'Curr::'}; 236 237 is $pet->speak, 'Woof!', 238 'deleting a stash from its parent stash resets caches of substashes'; 239 240 undef $life_raft; 241 is $pet->speak, 'Woof!', 242 'the deleted substash is gone completely when freed'; 243} 244 245# [perl #77358] 246fresh_perl_is 247 q~#!perl -w 248 @Pet::ISA = "Tike"; 249 @Tike::ISA = "Barker"; 250 251 sub Barker::speak { print "Woof!\n" } 252 sub Latrator::speak { print "Bow-wow!\n" } 253 254 my $pet = bless [], "Pet"; 255 256 $pet->speak; 257 258 sub Dog::speak { print "Hello.\n" } # strange dog! 259 @Dog::ISA = 'Latrator'; 260 *Tike:: = delete $::{'Dog::'}; 261 262 $pet->speak; 263 ~, 264 "Woof!\nHello.\n", 265 { stderr => 1 }, 266 "Assigning a nameless package over one w/subclasses updates isa caches"; 267 268# mro_package_moved needs to make a distinction between replaced and 269# assigned stashes when keeping track of what it has seen so far. 270no warnings; { 271 no strict 'refs'; 272 273 sub bar::blonk::blonk::phoo { "bbb" } 274 sub veclum::phoo { "lasrevinu" } 275 @feedlebomp::ISA = qw 'phoo::blonk::blonk veclum'; 276 *phoo::baz:: = *bar::blonk::; # now bar::blonk:: is on both sides 277 *phoo:: = *bar::; # here bar::blonk:: is both deleted and added 278 *bar:: = *boo::; # now it is only known as phoo::blonk:: 279 280 # At this point, before the bug was fixed, %phoo::blonk::blonk:: ended 281 # up with no effective name, allowing it to be deleted without updating 282 # its subclasses’ caches. 283 284 my $accum = ''; 285 286 $accum .= 'feedlebomp'->phoo; # bbb 287 delete ${"phoo::blonk::"}{"blonk::"}; 288 $accum .= 'feedlebomp'->phoo; # bbb (Oops!) 289 @feedlebomp::ISA = @feedlebomp::ISA; 290 $accum .= 'feedlebomp'->phoo; # lasrevinu 291 292 is $accum, 'bbblasrevinulasrevinu', 293 'nested classes deleted & added simultaneously'; 294} 295use warnings; 296 297# mro_package_moved needs to check for self-referential packages. 298# This broke Text::Template [perl #78362]. 299watchdog 3; 300*foo:: = \%::; 301*Acme::META::Acme:: = \*Acme::; # indirect self-reference 302pass("mro_package_moved and self-referential packages"); 303 304# Deleting a glob whose name does not indicate its location in the symbol 305# table but which nonetheless *is* in the symbol table. 306{ 307 no strict refs=>; 308 no warnings; 309 @one::more::ISA = "four"; 310 sub four::womp { "aoeaa" } 311 *two:: = *one::; 312 delete $::{"one::"}; 313 @Childclass::ISA = 'two::more'; 314 my $accum = 'Childclass'->womp . '-'; 315 my $life_raft = delete ${"two::"}{"more::"}; 316 $accum .= eval { 'Childclass'->womp } // '<undef>'; 317 is $accum, 'aoeaa-<undef>', 318 'Deleting globs whose loc in the symtab differs from gv_fullname' 319} 320 321# Pathological test for undeffing a stash that has an alias. 322*Ghelp:: = *Neen::; 323@Subclass::ISA = 'Ghelp'; 324undef %Ghelp::; 325sub Frelp::womp { "clumpren" } 326eval ' 327 $Neen::whatever++; 328 @Neen::ISA = "Frelp"; 329'; 330is eval { 'Subclass'->womp }, 'clumpren', 331 'Changes to @ISA after undef via original name'; 332undef %Ghelp::; 333eval ' 334 $Ghelp::whatever++; 335 @Ghelp::ISA = "Frelp"; 336'; 337is eval { 'Subclass'->womp }, 'clumpren', 338 'Changes to @ISA after undef via alias'; 339 340 341# Packages whose containing stashes have aliases must lose all names cor- 342# responding to that container when detached. 343{ 344 {package smare::baz} # autovivify 345 *phring:: = *smare::; # smare::baz now also named phring::baz 346 *bonk:: = delete $smare::{"baz::"}; 347 # In 5.13.7, it has now lost its smare::baz name (reverting to phring::baz 348 # as the effective name), and gained bonk as an alias. 349 # In 5.13.8, both smare::baz *and* phring::baz names are deleted. 350 351 # Make some methods 352 no strict 'refs'; 353 *{"phring::baz::frump"} = sub { "hello" }; 354 sub frumper::frump { "good bye" }; 355 356 @brumkin::ISA = qw "bonk frumper"; # now wrongly inherits from phring::baz 357 358 is frump brumkin, "good bye", 359 'detached stashes lose all names corresponding to the containing stash'; 360} 361 362# Crazy edge cases involving packages ending with a single : 363@Colon::ISA = 'Organ:'; # pun intended! 364bless [], "Organ:"; # autovivify the stash 365ok "Colon"->isa("Organ:"), 'class isa "class:"'; 366{ no strict 'refs'; *{"Organ:::"} = *Organ:: } 367ok "Colon"->isa("Organ"), 368 'isa(foo) when inheriting from "class:" which is an alias for foo'; 369{ 370 no warnings; 371 # The next line of code is *not* normative. If the structure changes, 372 # this line needs to change, too. 373 my $foo = delete $Organ::{":"}; 374 ok !Colon->isa("Organ"), 375 'class that isa "class:" no longer isa foo if "class:" has been deleted'; 376} 377@Colon::ISA = ':'; 378bless [], ":"; 379ok "Colon"->isa(":"), 'class isa ":"'; 380{ no strict 'refs'; *{":::"} = *Punctuation:: } 381ok "Colon"->isa("Punctuation"), 382 'isa(foo) when inheriting from ":" which is an alias for foo'; 383@Colon::ISA = 'Organ:'; 384bless [], "Organ:"; 385{ 386 no strict 'refs'; 387 my $life_raft = \%{"Organ:::"}; 388 *{"Organ:::"} = \%Organ::; 389 ok "Colon"->isa("Organ"), 390 'isa(foo) when inheriting from "class:" after hash-to-glob assignment'; 391} 392@Colon::ISA = 'O:'; 393bless [], "O:"; 394{ 395 no strict 'refs'; 396 my $life_raft = \%{"O:::"}; 397 *{"O:::"} = "Organ::"; 398 ok "Colon"->isa("Organ"), 399 'isa(foo) when inheriting from "class:" after string-to-glob assignment'; 400} 401 402@Bazo::ISA = "Fooo::bar"; 403sub Fooo::bar::ber { 'baz' } 404sub UNIVERSAL::ber { "black sheep" } 405Bazo->ber; 406local *Fooo:: = \%Baro::; 407{ 408 no warnings; 409 is 'Bazo'->ber, 'black sheep', 'localised *glob=$stashref assignment'; 410} 411 412# $Stash::{"entries::"} that are not globs. 413# These used to crash. 414$NotGlob::{"NotGlob::"} = 0; () = $NewNotGlob::NotGlob::; 415*NewNotGlob:: = *NotGlob::; 416pass( 417 "no crash when clobbering sub-'stash' whose parent stash entry is no GV" 418); 419