if (!tmpgv)
return NULL;
stash = GvHV(tmpgv);
- if (!HvNAME_get(stash))
+ if (!(flags & ~GV_NOADD_MASK) && !stash) return NULL;
+ if (!HvNAME_get(stash)) {
hv_name_set(stash, name, namelen, 0);
+
+ /* FIXME: This is a repeat of logic in gv_fetchpvn_flags */
+ /* If the containing stash has multiple effective
+ names, see that this one gets them, too. */
+ if (HvAUX(GvSTASH(tmpgv))->xhv_name_count)
+ mro_package_moved(stash, NULL, tmpgv, 1);
+ }
assert(stash);
return stash;
}
if (HeVAL(entry) && HvENAME_get(hv)) {
gv = (GV *)HeVAL(entry);
if (keysv) key = SvPV(keysv, klen);
- if (klen > 1 && key[klen-2] == ':' && key[klen-1] == ':'
+ if ((
+ (klen > 1 && key[klen-2] == ':' && key[klen-1] == ':')
+ ||
+ (klen == 1 && key[0] == ':')
+ )
&& (klen != 6 || hv!=PL_defstash || memNE(key,"main::",6))
&& SvTYPE(gv) == SVt_PVGV && (stash = GvHV((GV *)gv))
&& HvENAME_get(stash)) {
) {
STRLEN klen;
const char * const key = HePV(oentry,klen);
- if (klen > 1 && key[klen-1]==':' && key[klen-2]==':') {
+ if ((klen > 1 && key[klen-1]==':' && key[klen-2]==':')
+ || (klen == 1 && key[0] == ':')) {
mro_package_moved(
NULL, GvHV(HeVAL(oentry)),
(GV *)HeVAL(oentry), 0
) return;
}
assert(SvOOK(GvSTASH(gv)));
- assert(GvNAMELEN(gv) > 1);
+ assert(GvNAMELEN(gv));
assert(GvNAME(gv)[GvNAMELEN(gv) - 1] == ':');
- assert(GvNAME(gv)[GvNAMELEN(gv) - 2] == ':');
+ assert(GvNAMELEN(gv) == 1 || GvNAME(gv)[GvNAMELEN(gv) - 2] == ':');
name_count = HvAUX(GvSTASH(gv))->xhv_name_count;
if (!name_count) {
name_count = 1;
}
if (name_count == 1) {
if (HEK_LEN(*namep) == 4 && strnEQ(HEK_KEY(*namep), "main", 4)) {
- namesv = newSVpvs_flags("", SVs_TEMP);
+ namesv = GvNAMELEN(gv) == 1
+ ? newSVpvs_flags(":", SVs_TEMP)
+ : newSVpvs_flags("", SVs_TEMP);
}
else {
namesv = sv_2mortal(newSVhek(*namep));
- sv_catpvs(namesv, "::");
+ if (GvNAMELEN(gv) == 1) sv_catpvs(namesv, ":");
+ else sv_catpvs(namesv, "::");
}
- sv_catpvn(namesv, GvNAME(gv), GvNAMELEN(gv) - 2);
+ if (GvNAMELEN(gv) != 1)
+ sv_catpvn(namesv, GvNAME(gv), GvNAMELEN(gv) - 2);
/* skip trailing :: */
}
else {
namesv = sv_2mortal((SV *)newAV());
while (name_count--) {
if(HEK_LEN(*namep) == 4 && strnEQ(HEK_KEY(*namep), "main", 4)){
- aname = newSVpvs(""); namep++;
+ aname = GvNAMELEN(gv) == 1
+ ? newSVpvs(":")
+ : newSVpvs("");
+ namep++;
}
else {
aname = newSVhek(*namep++);
- sv_catpvs(aname, "::");
+ if (GvNAMELEN(gv) == 1) sv_catpvs(aname, ":");
+ else sv_catpvs(aname, "::");
}
- sv_catpvn(aname, GvNAME(gv), GvNAMELEN(gv) - 2);
+ if (GvNAMELEN(gv) != 1)
+ sv_catpvn(aname, GvNAME(gv), GvNAMELEN(gv) - 2);
/* skip trailing :: */
av_push((AV *)namesv, aname);
}
if (!isGV(HeVAL(entry))) continue;
key = hv_iterkey(entry, &len);
- if(len > 1 && key[len-2] == ':' && key[len-1] == ':') {
+ if ((len > 1 && key[len-2] == ':' && key[len-1] == ':')
+ || (len == 1 && key[0] == ':')) {
HV * const oldsubstash = GvHV(HeVAL(entry));
SV ** const stashentry
= stash ? hv_fetch(stash, key, len, 0) : NULL;
subname = sv_2mortal((SV *)newAV());
while (items--) {
aname = newSVsv(*svp++);
- sv_catpvs(aname, "::");
- sv_catpvn(aname, key, len-2);
+ if (len == 1)
+ sv_catpvs(aname, ":");
+ else {
+ sv_catpvs(aname, "::");
+ sv_catpvn(aname, key, len-2);
+ }
av_push((AV *)subname, aname);
}
}
else {
subname = sv_2mortal(newSVsv(namesv));
- sv_catpvs(subname, "::");
- sv_catpvn(subname, key, len-2);
+ if (len == 1) sv_catpvs(subname, ":");
+ else {
+ sv_catpvs(subname, "::");
+ sv_catpvn(subname, key, len-2);
+ }
}
mro_gather_and_rename(
stashes, seen_stashes,
if (!isGV(HeVAL(entry))) continue;
key = hv_iterkey(entry, &len);
- if(len > 1 && key[len-2] == ':' && key[len-1] == ':') {
+ if ((len > 1 && key[len-2] == ':' && key[len-1] == ':')
+ || (len == 1 && key[0] == ':')) {
HV *substash;
/* If this entry was seen when we iterated through the
subname = sv_2mortal((SV *)newAV());
while (items--) {
aname = newSVsv(*svp++);
- sv_catpvs(aname, "::");
- sv_catpvn(aname, key, len-2);
+ if (len == 1)
+ sv_catpvs(aname, ":");
+ else {
+ sv_catpvs(aname, "::");
+ sv_catpvn(aname, key, len-2);
+ }
av_push((AV *)subname, aname);
}
}
else {
subname = sv_2mortal(newSVsv(namesv));
- sv_catpvs(subname, "::");
- sv_catpvn(subname, key, len-2);
+ if (len == 1) sv_catpvs(subname, ":");
+ else {
+ sv_catpvs(subname, "::");
+ sv_catpvn(subname, key, len-2);
+ }
}
mro_gather_and_rename(
stashes, seen_stashes,
mro_changes = 2;
else {
const STRLEN len = GvNAMELEN(dstr);
- if (len > 1 && name[len-2] == ':' && name[len-1] == ':') {
+ if ((len > 1 && name[len-2] == ':' && name[len-1] == ':')
+ || (len == 1 && name[0] == ':')) {
mro_changes = 3;
/* Set aside the old stash, so we can reset isa caches on
const char * const name = GvNAME((GV*)dstr);
const STRLEN len = GvNAMELEN(dstr);
if (
- len > 1 && name[len-2] == ':' && name[len-1] == ':'
+ (
+ (len > 1 && name[len-2] == ':' && name[len-1] == ':')
+ || (len == 1 && name[0] == ':')
+ )
&& (!dref || HvENAME_get(dref))
) {
mro_package_moved(
const STRLEN len = GvNAMELEN(dstr);
HV *old_stash = NULL;
bool reset_isa = FALSE;
- if (len > 1 && name[len-2] == ':' && name[len-1] == ':') {
+ if ((len > 1 && name[len-2] == ':' && name[len-1] == ':')
+ || (len == 1 && name[0] == ':')) {
/* Set aside the old stash, so we can reset isa caches
on its subclasses. */
if((old_stash = GvHV(dstr))) {
use strict;
use warnings;
-plan(tests => 39);
+plan(tests => 52);
{
package New;
code => '*clone:: = \%outer::',
},
) {
- for my $tail ('inner', 'inner::', 'inner::::') {
+ for my $tail ('inner', 'inner::', 'inner:::', 'inner::::') {
fresh_perl_is
q~
my $tail = shift;
@left::ISA = "outer::$tail";
@right::ISA = "clone::$tail";
- eval "package outer::$tail";
+ bless [], "outer::$tail"; # autovivify the stash
__code__;
__code__;
- eval qq{package outer::$tail};
+ bless [], "outer::$tail";
print "ok 1", "\n" if left->isa("clone::$tail");
print "ok 2", "\n" if right->isa("outer::$tail");
is frump brumkin, "good bye",
'detached stashes lose all names corresponding to the containing stash';
}
+
+# Crazy edge cases involving packages ending with a single :
+@Colon::ISA = 'Organ:'; # pun intended!
+bless [], "Organ:"; # autovivify the stash
+ok "Colon"->isa("Organ:"), 'class isa "class:"';
+{ no strict 'refs'; *{"Organ:::"} = *Organ:: }
+ok "Colon"->isa("Organ"),
+ 'isa(foo) when inheriting from "class:" which is an alias for foo';
+{
+ no warnings;
+ # The next line of code is *not* normative. If the structure changes,
+ # this line needs to change, too.
+ my $foo = delete $Organ::{":"};
+ ok !Colon->isa("Organ"),
+ 'class that isa "class:" no longer isa foo if "class:" has been deleted';
+}
+@Colon::ISA = ':';
+bless [], ":";
+ok "Colon"->isa(":"), 'class isa ":"';
+{ no strict 'refs'; *{":::"} = *Punctuation:: }
+ok "Colon"->isa("Punctuation"),
+ 'isa(foo) when inheriting from ":" which is an alias for foo';
+@Colon::ISA = 'Organ:';
+bless [], "Organ:";
+{
+ no strict 'refs';
+ my $life_raft = \%{"Organ:::"};
+ *{"Organ:::"} = \%Organ::;
+ ok "Colon"->isa("Organ"),
+ 'isa(foo) when inheriting from "class:" after hash-to-glob assignment';
+}
+@Colon::ISA = 'O:';
+bless [], "O:";
+{
+ no strict 'refs';
+ my $life_raft = \%{"O:::"};
+ *{"O:::"} = "Organ::";
+ ok "Colon"->isa("Organ"),
+ 'isa(foo) when inheriting from "class:" after string-to-glob assignment';
+}
+
+
require "./test.pl";
}
-plan tests => 124;
+plan tests => 125;
$a = {};
bless $a, "Bob";
# This segfaulted in a blead.
fresh_perl_is('package Foo; Foo->VERSION; print "ok"', 'ok');
+# So did this.
+fresh_perl_is('$:; UNIVERSAL::isa(":","Unicode::String");print "ok"','ok');
+
package Foo;
sub DOES { 1 }