1#!./perl
2
3BEGIN {
4    $ENV{PERL_UNICODE} = 0;
5    unless (-d 'blib') {
6        chdir 't' if -d 't';
7    }
8    require q(./test.pl);
9    set_up_inc('../lib');
10}
11
12use strict;
13use warnings;
14use utf8;
15use open qw( :utf8 :std );
16
17plan(tests => 52);
18
19{
20    packageeẁ;
21    use strict;
22    use warnings;
23
24    packagelㄉ;
25    use strict;
26    use warnings;
27
28    {
29      no strict 'refs';
30      *{'ऑlㄉ::'} = *{'Neẁ::'};
31    }
32}
33
34ok (ऑlㄉ->isa(Neẁ::), 'ऑlㄉ inherits from Neẁ');
35ok (Neẁ->isa(ऑlㄉ::), 'Neẁ inherits from ऑlㄉ');
36
37object_ok (bless ({}, ऑlㄉ::), Neẁ::, 'ऑlㄉ object');
38object_ok (bless ({}, Neẁ::), ऑlㄉ::, 'Neẁ object');
39
40
41# Test that replacing a package by assigning to an existing glob
42# invalidates the isa caches
43for(
44 {
45   name => 'assigning a glob to a glob',
46   code => '$life_raft = $::{"lㅔf::"}; *lㅔf:: = $::{"릭Ⱶᵀ::"}',
47 },
48 {
49   name => 'assigning a string to a glob',
50   code => '$life_raft = $::{"lㅔf::"}; *lㅔf:: = "릭Ⱶᵀ::"',
51 },
52 {
53   name => 'assigning a stashref to a glob',
54   code => '$life_raft = \%lㅔf::; *lㅔf:: = \%릭Ⱶᵀ::',
55 },
56) {
57my $prog =    q~
58     BEGIN {
59         unless (-d 'blib') {
60             chdir 't' if -d 't';
61             @INC = '../lib';
62         }
63     }
64     use utf8;
65     use open qw( :utf8 :std );
66
67     @숩cਲꩋ::ISA = "lㅔf";
68     @lㅔf::ISA = "톺ĺФț";
69
70     sub 톺ĺФț::Sᑊeಅḱ { "Woof!" }
71     sub ᴖ릭ᚽʇ::Sᑊeಅḱ { "Bow-wow!" }
72
73     my $thing = bless [], "숩cਲꩋ";
74
75     # mro_package_moved needs to know to skip non-globs
76     $릭Ⱶᵀ::{"ᚷꝆエcƙ::"} = 3;
77
78     @릭Ⱶᵀ::ISA = 'ᴖ릭ᚽʇ';
79     my $life_raft;
80    __code__;
81
82     print $thing->Sᑊeಅḱ, "\n";
83
84     undef $life_raft;
85     print $thing->Sᑊeಅḱ, "\n";
86   ~ =~ s\__code__\$$_{code}\r; #\
87utf8::encode($prog);
88 fresh_perl_is
89  $prog,
90  "Bow-wow!\nBow-wow!\n",
91   {},
92  "replacing packages by $$_{name} updates isa caches";
93}
94
95# Similar test, but with nested packages
96#
97#  톺ĺФț (Woof)    ᴖ릭ᚽʇ (Bow-wow)
98#      |                 |
99#  lㅔf::Side   <-   릭Ⱶᵀ::Side
100#      |
101#   숩cਲꩋ
102#
103# This test assigns 릭Ⱶᵀ:: to lㅔf::, indirectly making lㅔf::Side an
104# alias to 릭Ⱶᵀ::Side (following the arrow in the diagram).
105for(
106 {
107   name => 'assigning a glob to a glob',
108   code => '$life_raft = $::{"lㅔf::"}; *lㅔf:: = $::{"릭Ⱶᵀ::"}',
109 },
110 {
111   name => 'assigning a string to a glob',
112   code => '$life_raft = $::{"lㅔf::"}; *lㅔf:: = "릭Ⱶᵀ::"',
113 },
114 {
115   name => 'assigning a stashref to a glob',
116   code => '$life_raft = \%lㅔf::; *lㅔf:: = \%릭Ⱶᵀ::',
117 },
118) {
119 my $prog = q~
120     BEGIN {
121         unless (-d 'blib') {
122             chdir 't' if -d 't';
123             @INC = '../lib';
124         }
125     }
126     use utf8;
127     use open qw( :utf8 :std );
128     @숩cਲꩋ::ISA = "lㅔf::Side";
129     @lㅔf::Side::ISA = "톺ĺФț";
130
131     sub 톺ĺФț::Sᑊeಅḱ { "Woof!" }
132     sub ᴖ릭ᚽʇ::Sᑊeಅḱ { "Bow-wow!" }
133
134     my $thing = bless [], "숩cਲꩋ";
135
136     @릭Ⱶᵀ::Side::ISA = 'ᴖ릭ᚽʇ';
137     my $life_raft;
138    __code__;
139
140     print $thing->Sᑊeಅḱ, "\n";
141
142     undef $life_raft;
143     print $thing->Sᑊeಅḱ, "\n";
144   ~ =~ s\__code__\$$_{code}\r;
145 utf8::encode($prog);
146
147 fresh_perl_is
148  $prog,
149  "Bow-wow!\nBow-wow!\n",
150   {},
151  "replacing nested packages by $$_{name} updates isa caches";
152}
153
154# Another nested package test, in which the isa cache needs to be reset on
155# the subclass of a package that does not exist.
156#
157# Parenthesized packages do not exist.
158#
159#  ɵűʇㄦ::인ንʵ    ( cฬnए::인ንʵ )
160#       |                 |
161#     Lфť              R익hȚ
162#
163#        ɵűʇㄦ  ->  cฬnए
164#
165# This test assigns ɵűʇㄦ:: to cฬnए::, making cฬnए::인ንʵ an alias to
166# ɵűʇㄦ::인ንʵ.
167#
168# Then we also run the test again, but without ɵűʇㄦ::인ንʵ
169for(
170 {
171   name => 'assigning a glob to a glob',
172   code => '*cฬnए:: = *ɵűʇㄦ::',
173 },
174 {
175   name => 'assigning a string to a glob',
176   code => '*cฬnए:: = "ɵűʇㄦ::"',
177 },
178 {
179   name => 'assigning a stashref to a glob',
180   code => '*cฬnए:: = \%ɵűʇㄦ::',
181 },
182) {
183 for my $tail ('인ንʵ', '인ንʵ::', '인ንʵ:::', '인ንʵ::::') {
184  my $prog =     q~
185     BEGIN {
186         unless (-d 'blib') {
187             chdir 't' if -d 't';
188             @INC = '../lib';
189         }
190     }
191      use utf8;
192      use open qw( :utf8 :std );
193      use Encode ();
194
195      if (grep /\P{ASCII}/, @ARGV) {
196        @ARGV = map { Encode::decode("UTF-8", $_) } @ARGV;
197      }
198
199      my $tail = shift;
200      @Lфť::ISA = "ɵűʇㄦ::$tail";
201      @RhȚ::ISA = "cฬnए::$tail";
202      bless [], "ɵűʇㄦ::$tail"; # autovivify the stash
203
204     __code__;
205
206      print "ok 1", "\n" if Lфť->isa("cฬnए::$tail");
207      print "ok 2", "\n" if RhȚ->isa("ɵűʇㄦ::$tail");
208      print "ok 3", "\n" if RhȚ->isa("cฬnए::$tail");
209      print "ok 4", "\n" if Lфť->isa("ɵűʇㄦ::$tail");
210    ~ =~ s\__code__\$$_{code}\r;
211  utf8::encode($prog);
212  fresh_perl_is
213   $prog,
214   "ok 1\nok 2\nok 3\nok 4\n",
215    { args => [$tail] },
216   "replacing nonexistent nested packages by $$_{name} updates isa caches"
217     ." ($tail)";
218
219  # Same test but with the subpackage autovivified after the assignment
220  $prog =     q~
221      BEGIN {
222         unless (-d 'blib') {
223             chdir 't' if -d 't';
224             @INC = '../lib';
225         }
226      }
227      use utf8;
228      use open qw( :utf8 :std );
229      use Encode ();
230
231      if (grep /\P{ASCII}/, @ARGV) {
232        @ARGV = map { Encode::decode("UTF-8", $_) } @ARGV;
233      }
234
235      my $tail = shift;
236      @Lфť::ISA = "ɵűʇㄦ::$tail";
237      @RhȚ::ISA = "cฬnए::$tail";
238
239     __code__;
240
241      bless [], "ɵűʇㄦ::$tail";
242
243      print "ok 1", "\n" if Lфť->isa("cฬnए::$tail");
244      print "ok 2", "\n" if RhȚ->isa("ɵűʇㄦ::$tail");
245      print "ok 3", "\n" if RhȚ->isa("cฬnए::$tail");
246      print "ok 4", "\n" if Lфť->isa("ɵűʇㄦ::$tail");
247    ~ =~ s\__code__\$$_{code}\r;
248  utf8::encode($prog);
249  fresh_perl_is
250   $prog,
251   "ok 1\nok 2\nok 3\nok 4\n",
252    { args => [$tail] },
253   "Giving nonexistent packages multiple effective names by $$_{name}"
254     . " ($tail)";
255 }
256}
257
258no warnings; # temporary; there seems to be a scoping bug, as this does not
259             # work when placed in the blocks below
260
261# Test that deleting stash elements containing
262# subpackages also invalidates the isa cache.
263# Maybe this does not belong in package_aliases.t, but it is closely
264# related to the tests immediately preceding.
265{
266 @ቹऋ::ISA = ("Cuȓ", "ฮンᛞ");
267 @Cuȓ::ISA = "Hyḹ앛Ҭテ";
268
269 sub Hyḹ앛Ҭテ::Sᑊeಅḱ { "Arff!" }
270 sub ฮンᛞ::Sᑊeಅḱ { "Woof!" }
271
272 my $pet = bless [], "ቹऋ";
273
274 my $life_raft = delete $::{'Cuȓ::'};
275
276 is $pet->Sᑊeಅḱ, 'Woof!',
277  'deleting a stash from its parent stash invalidates the isa caches';
278
279 undef $life_raft;
280 is $pet->Sᑊeಅḱ, 'Woof!',
281  'the deleted stash is gone completely when freed';
282}
283# Same thing, but with nested packages
284{
285 @펱ᑦ::ISA = ("Cuȓȓ::Cuȓȓ::Cuȓȓ", "ɥwn");
286 @Cuȓȓ::Cuȓȓ::Cuȓȓ::ISA = "lȺt랕ᚖ";
287
288 sub lȺt랕ᚖ::Sᑊeಅḱ { "Arff!" }
289 sub ɥwn::Sᑊeಅḱ { "Woof!" }
290
291 my $pet = bless [], "펱ᑦ";
292
293 my $life_raft = delete $::{'Cuȓȓ::'};
294
295 is $pet->Sᑊeಅḱ, 'Woof!',
296  'deleting a stash from its parent stash resets caches of substashes';
297
298 undef $life_raft;
299 is $pet->Sᑊeಅḱ, 'Woof!',
300  'the deleted substash is gone completely when freed';
301}
302
303# [perl #77358]
304my $prog =    q~#!perl -w
305     BEGIN {
306         unless (-d 'blib') {
307             chdir 't' if -d 't';
308             @INC = '../lib';
309         }
310     }
311     use utf8;
312     use open qw( :utf8 :std );
313     @펱ᑦ::ISA = "T잌ዕ";
314     @T잌ዕ::ISA = "Bᛆヶṝ";
315
316     sub Bᛆヶṝ::Sᑊeಅḱ { print "Woof!\n" }
317     sub lȺt랕ᚖ::Sᑊeಅḱ { print "Bow-wow!\n" }
318
319     my $pet = bless [], "펱ᑦ";
320
321     $pet->Sᑊeಅḱ;
322
323     sub ດƓ::Sᑊeಅḱ { print "Hello.\n" } # strange ດƓ!
324     @ດƓ::ISA = 'lȺt랕ᚖ';
325     *T잌ዕ:: = delete $::{'ດƓ::'};
326
327     $pet->Sᑊeಅḱ;
328   ~;
329utf8::encode($prog);
330fresh_perl_is
331  $prog,
332  "Woof!\nHello.\n",
333   { stderr => 1 },
334  "Assigning a nameless package over one w/subclasses updates isa caches";
335
336# mro_package_moved needs to make a distinction between replaced and
337# assigned stashes when keeping track of what it has seen so far.
338no warnings; {
339    no strict 'refs';
340
341    sub ʉ::bnǩ::bnǩ::ພo { "bbb" }
342    subel움::ພo { "lasrevinu" }
343    @ݏ엗Ƚeᵬૐᵖ::ISA = qw 'ພo::bᓗnǩ::bᓗnǩ ᵛeↄl움';
344    *ພo::ବㄗ:: = *ʉ::bnǩ::;   # now ʉ::bᓗnǩ:: is on both sides
345    *ພo:: = *ʉ::;         # here ʉ::bᓗnǩ:: is both deleted and added
346    *ʉ:: = *ቦᵕ::;          # now it is only known as ພo::bᓗnǩ::
347
348    # At this point, before the bug was fixed, %ພo::bᓗnǩ::bᓗnǩ:: ended
349    # up with no effective name, allowing it to be deleted without updating
350    # its subclasses’ caches.
351
352    my $accum = '';
353
354    $accum .= 'ݏ엗Ƚeᵬૐᵖ'->ພo;          # bbb
355    delete ${"ພo::bᓗnǩ::"}{"bᓗnǩ::"};
356    $accum .= 'ݏ엗Ƚeᵬૐᵖ'->ພo;          # bbb (Oops!)
357    @ݏ엗Ƚeᵬૐᵖ::ISA = @ݏ엗Ƚeᵬૐᵖ::ISA;
358    $accum .= 'ݏ엗Ƚeᵬૐᵖ'->ພo;          # lasrevinu
359
360    is $accum, 'bbblasrevinulasrevinu',
361      'nested classes deleted & added simultaneously';
362}
363use warnings;
364
365# mro_package_moved needs to check for self-referential packages.
366# This broke Text::Template [perl #78362].
367watchdog 3;
368*ᕘ:: = \%::;
369*Ame::Mῌ::Ame:: = \*Ame::; # indirect self-reference
370pass("mro_package_moved and self-referential packages");
371
372# Deleting a glob whose name does not indicate its location in the symbol
373# table but which nonetheless *is* in the symbol table.
374{
375    no strict refs=>;
376    no warnings;
377    @ოƐ::mrェ::ISA = "foᚒ";
378    sub foᚒ::ວmᑊ { "aoeaa" }
379    *ťວ:: = *ოƐ::;
380    delete $::{"ოƐ::"};
381    @Cdl았::ISA = 'ťວ::mഒrェ';
382    my $accum = 'C힐dᒡl았'->ວmᑊ . '-';
383    my $life_raft = delete ${"ťວ::"}{"mഒrェ::"};
384    $accum .= eval { 'C힐dᒡl았'->ວmᑊ } // '<undef>';
385    is $accum, 'aoeaa-<undef>',
386     'Deleting globs whose loc in the symtab differs from gv_fullname'
387}
388
389# Pathological test for undeffing a stash that has an alias.
390*ᵍh엞:: = *ኔƞ::;
391@숩cਲꩋ::ISA = 'ᵍh엞';
392undef %ᵍh엞::;
393sub F렐ᛔ::ວmᑊ { "clumpren" }
394eval '
395  $ኔƞ::whatever++;
396  @ኔƞ::ISA = "F렐ᛔ";
397';
398is eval { '숩cਲꩋ'->ວmᑊ }, 'clumpren',
399 'Changes to @ISA after undef via original name';
400undef %ᵍh엞::;
401eval '
402  $ᵍh엞::whatever++;
403  @ᵍh엞::ISA = "F렐ᛔ";
404';
405is eval { '숩cਲꩋ'->ວmᑊ }, 'clumpren',
406 'Changes to @ISA after undef via alias';
407
408
409# Packages whose containing stashes have aliases must lose all names cor-
410# responding to that container when detached.
411{
412 {package śmᛅḙ::በɀ} # autovivify
413 *pḢ린ᚷ:: = *śmᛅḙ::;  # śmᛅḙ::በɀ now also named pḢ린ᚷ::በɀ
414 *본:: = deletemᛅḙ::{"በɀ::"};
415 # In 5.13.7, it has now lost its śmᛅḙ::በɀ name (reverting to pḢ린ᚷ::በɀ
416 # as the effective name), and gained 본 as an alias.
417 # In 5.13.8, both śmᛅḙ::በɀ *and* pḢ린ᚷ::በɀ names are deleted.
418
419 # Make some methods
420 no strict 'refs';
421 *{"pḢ린ᚷ::በɀ::fฤmᛈ"} = sub { "hello" };
422 sub Fルmፕṟ::fmᛈ { "good bye" };
423
424 @ᵇるᣘ킨::ISA = qw "본 Fルmፕṟ"; # now wrongly inherits from pḢ린ᚷ::በɀ
425
426 is fmᛈ ᵇるᣘ킨, "good bye",
427  'detached stashes lose all names corresponding to the containing stash';
428}
429
430# Crazy edge cases involving packages ending with a single :
431@촐oン::ISA = 'ᚖგ:'; # pun intended!
432bless [], "ᚖგ:"; # autovivify the stash
433ok "촐oン"->isa("ᚖგ:"), 'class isa "class:"';
434{ no strict 'refs'; *{"ᚖგ:::"} = *ᚖგ:: }
435ok "촐oン"->isa("ᚖგ"),
436 'isa(ᕘ) when inheriting from "class:" which is an alias for ᕘ';
437{
438 no warnings;
439 # The next line of code is *not* normative. If the structure changes,
440 # this line needs to change, too.
441 my $ᕘ = delete $ᚖგ::{":"};
442 ok !촐oン->isa("ᚖგ"),
443  'class that isa "class:" no longer isa ᕘ if "class:" has been deleted';
444}
445@촐oン::ISA = ':';
446bless [], ":";
447ok "촐oン"->isa(":"), 'class isa ":"';
448{ no strict 'refs'; *{":::"} = *ፑňṪu앝ȋ온:: }
449ok "촐oン"->isa("ፑňṪu앝ȋ온"),
450 'isa(ᕘ) when inheriting from ":" which is an alias for ᕘ';
451@촐oン::ISA = 'ᚖგ:';
452bless [], "ᚖგ:";
453{
454 no strict 'refs';
455 my $life_raft = \%{"ᚖგ:::"};
456 *{"ᚖგ:::"} = \%ᚖგ::;
457 ok "촐oン"->isa("ᚖგ"),
458  'isa(ᕘ) when inheriting from "class:" after hash-to-glob assignment';
459}
460@촐oン::ISA = 'ŏ:';
461bless [], "ŏ:";
462{
463 no strict 'refs';
464 my $life_raft = \%{"ŏ:::"};
465 *{"ŏ:::"} = "ᚖგ::";
466 ok "촐oン"->isa("ᚖგ"),
467  'isa(ᕘ) when inheriting from "class:" after string-to-glob assignment';
468}
469=cut
470