I32 level;
{
AV* av;
- GV* topgv;
+ GV* topgv = NULL;
GV* gv;
GV** gvp;
HV* lastchance;
if (!stash)
return 0;
- if (level > 100)
+ if ((level > 100) || (level < -100))
croak("Recursive inheritance detected");
- gvp = (GV**)hv_fetch(stash, name, len, TRUE);
+ gvp = (GV**)hv_fetch(stash, name, len, (level >= 0));
DEBUG_o( deb("Looking for method %s in package %s\n",name,HvNAME(stash)) );
+ if (!gvp) goto recurse;
+
topgv = *gvp;
if (SvTYPE(topgv) != SVt_PVGV)
gv_init(topgv, stash, name, len, TRUE);
}
/* Now cv = 0, and there is no cv in topgv. */
+ recurse:
gvp = (GV**)hv_fetch(stash,"ISA",3,FALSE);
if (gvp && (gv = *gvp) != (GV*)&sv_undef && (av = GvAV(gv))) {
SV** svp = AvARRAY(av);
SvPVX(sv), HvNAME(stash));
continue;
}
- gv = gv_fetchmeth(basestash, name, len, level + 1);
- if (gv) {
+ gv = gv_fetchmeth(basestash, name, len, level + (level >= 0 ? 1 : -1));
+ if (gv && topgv) {
GvCV(topgv) = GvCV(gv); /* cache the CV */
GvCVGEN(topgv) = sub_generation; /* valid for now */
SvREFCNT_inc(GvCV(gv));
return gv;
- }
+ } else if (gv) return gv;
}
}
- if (!level) {
+ if ((level == 0) || (level == -1)) { /* topgv is present. */
if (lastchance = gv_stashpvn("UNIVERSAL", 9, FALSE)) {
- if (gv = gv_fetchmeth(lastchance, name, len, level + 1)) {
+ if (gv = gv_fetchmeth(lastchance, name, len, level + (level >= 0 ? 1 : -1))) {
GvCV(topgv) = GvCV(gv); /* cache the CV */
GvCVGEN(topgv) = sub_generation; /* valid for now */
SvREFCNT_inc(GvCV(gv));
*buf = '('; /* A cooky: "(". */
strcpy(buf + 1, cp);
- gv = gv_fetchmeth(stash, buf, strlen(buf), 0); /* fills the stash! */
- if(gv && (cv = GvCV(gv))) filled = 1;
+ DEBUG_o( deb("Checking overloading of `%s' in package `%.256s'\n",
+ cp, HvNAME(stash)) );
+ gv = gv_fetchmeth(stash, buf, strlen(buf), -1); /* no filling stash! */
+ if(gv && (cv = GvCV(gv))) {
+ char *name = buf;
+ if (GvNAMELEN(CvGV(cv)) == 3 && strEQ(GvNAME(CvGV(cv)), "nil")
+ && strEQ(HvNAME(GvSTASH(CvGV(cv))), "overload")) {
+ /* GvSV contains the name of the method. */
+ GV *ngv;
+
+ DEBUG_o( deb("Resolving method `%.256s' for overloaded `%s' in package `%.256s'\n",
+ SvPV(GvSV(gv), na), cp, HvNAME(stash)) );
+ if (SvPOK(GvSV(gv))
+ && (ngv = gv_fetchmethod(stash, SvPVX(GvSV(gv))))) {
+ name = SvPVX(GvSV(gv));
+ cv = GvCV(gv = ngv);
+ } else {
+ /* Can be an import stub (created by `can'). */
+ if (GvCVGEN(gv)) {
+ croak("Stub found while resolving method `%.256s' overloading `%s' in package `%.256s'",
+ (SvPOK(GvSV(gv)) ? SvPVX(GvSV(gv)) : "???" ),
+ cp, HvNAME(stash));
+ } else
+ croak("Cannot resolve method `%.256s' overloading `%s' in package `%.256s'",
+ (SvPOK(GvSV(gv)) ? SvPVX(GvSV(gv)) : "???" ),
+ cp, HvNAME(stash));
+ }
+ /* If the sub is only a stub then we may have a gv to AUTOLOAD */
+ gv = (GV*)*hv_fetch(GvSTASH(gv), name, strlen(name), TRUE);
+ cv = GvCV(gv);
+ }
+ DEBUG_o( deb("Overloading `%s' in package `%.256s' via `%.256s::%.256s' \n",
+ cp, HvNAME(stash), HvNAME(GvSTASH(CvGV(cv))),
+ GvNAME(CvGV(cv))) );
+ filled = 1;
+ }
#endif
amt.table[i]=(CV*)SvREFCNT_inc(cv);
}
case dec_amg:
SvSetSV(left,res); return left;
case not_amg:
-ans=!SvOK(res); break;
+ ans=!SvOK(res); break;
}
return ans? &sv_yes: &sv_no;
} else if (method==copy_amg) {
} else {
$sub = $arg{$_};
if (not ref $sub and $sub !~ /::/) {
- $sub = "${'package'}::$sub";
+ $ {$package . "::(" . $_} = $sub;
+ $sub = \&nil;
}
#print STDERR "Setting `$ {'package'}::\cO$_' to \\&`$sub'.\n";
*{$package . "::(" . $_} = \&{ $sub };
$package->can('()');
}
+sub ov_method {
+ my $globref = shift;
+ return undef unless $globref;
+ my $sub = \&{*$globref};
+ return $sub if $sub ne \&nil;
+ return shift->can($ {*$globref});
+}
+
sub OverloadedStringify {
my $package = shift;
$package = ref $package if ref $package;
- $package->can('(""')
+ #$package->can('(""')
+ ov_method mycan($package, '(""'), $package;
}
sub Method {
my $package = shift;
$package = ref $package if ref $package;
- $package->can('(' . shift)
+ #my $meth = $package->can('(' . shift);
+ ov_method mycan($package, '(' . shift), $package;
+ #return $meth if $meth ne \&nil;
+ #return $ {*{$meth}};
}
sub AddrRef {
"$_[0]";
}
+sub mycan { # Real can would leave stubs.
+ my ($package, $meth) = @_;
+ return \*{$package . "::$meth"} if defined &{$package . "::$meth"};
+ my $p;
+ foreach $p (@{"${package}::ISA"}) {
+ my $out = mycan($p, $meth);
+ return $out if $out;
+ }
+ return undef;
+}
+
1;
__END__
sub { *{"Oscalar::$AUTOLOAD"} = sub {"_!_" . shift() . "_!_"} ;
goto &{"Oscalar::$AUTOLOAD"}};
-eval "package Oscalar; use overload '~' => 'comple'";
+eval "package Oscalar; sub comple; use overload '~' => 'comple'";
$na = eval { ~$a }; # Hash was not updated
test($@ =~ /no method found/); # 97
bless \$x, Oscalar;
$na = eval { ~$a }; # Hash updated
+warn "`$na', $@" if $@;
test !$@; # 98
test($na eq '_!_xx_!_'); # 99
test !$@; # 101
test($na eq '_!_xx_!_'); # 102
-eval "package Oscalar; use overload '>>' => 'rshft'";
+eval "package Oscalar; sub rshft; use overload '>>' => 'rshft'";
$na = eval { $aI >> 1 }; # Hash was not updated
test($@ =~ /no method found/); # 103
test !$@; # 104
test($na eq '_!_xx_!_'); # 105
+# warn overload::Method($a, '0+'), "\n";
test (overload::Method($a, '0+') eq \&Oscalar::numify); # 106
test (overload::Method($aI,'0+') eq \&Oscalar::numify); # 107
test (overload::Overloaded($aI)); # 108
test (overload::StrVal($aI) =~ /^OscalarI=SCALAR\(0x[\da-fA-F]+\)$/); # 112
test (overload::StrVal(\$aI) eq "@{[\$aI]}"); # 113
+# Check overloading by methods (specified deep in the ISA tree).
+{
+ package OscalarII;
+ @ISA = 'OscalarI';
+ sub Oscalar::lshft {"_<<_" . shift() . "_<<_"}
+ eval "package OscalarI; use overload '<<' => 'lshft', '|' => 'lshft'";
+}
+
+$aaII = "087";
+$aII = \$aaII;
+bless $aII, 'OscalarII';
+bless \$fake, 'OscalarI'; # update the hash
+test(($aI | 3) eq '_<<_xx_<<_'); # 114
+# warn $aII << 3;
+test(($aII << 3) eq '_<<_087_<<_'); # 115
+
# Last test is:
-sub last {113}
+sub last {115}