develooper Front page | perl.perl5.porters | Postings from February 2009

Re: method names clash by importing: next step

Thread Previous | Thread Next
From:
Dmitry Karasik
Date:
February 24, 2009 03:04
Subject:
Re: method names clash by importing: next step
Message ID:
84ljrwm6z6.fsf@tetsuo.karasik.eu.org
	Hi Rafael!

On 23 фев 09 at 22:34, "Rafael" (Rafael Garcia-Suarez) wrote:

 Rafael> Actually I don't think that we need an option to turn that
 Rafael> behaviour on.  The :method attribute is unused, undocumented, and
 Rafael> obscure. We can claim it for new features. The backwards
 Rafael> compatibility breakage would be pretty minimal, I think.

I partly agree because I share your opinion about :method being an
obscure feature. But I also partly disagree because I usually plan for
orthogonality, so if we should have a new feature that disallows exported names
from participating in method lookup, possibly we should have a way
to allow them too.

On the backward compatibility issue, I have no opinion. 

 Rafael> I don't like per-object behaviour. Especially in languages like
 Rafael> perl that allow objects to change its class at runtime. I'd say,
 Rafael> go for the solution that takes less memory in most cases: possibly
 Rafael> by extending the stash itself.

I can't say I pesonally like dynamic reblessing because objects change the 
class name too, so it's not possible to compare ref($obj) after reblessing is
done. But it wouldn't matter if per-object behavior will be implemented
or not, I rather thought of it as a nice benefit that would come for free.

 Rafael> Can you explain a bit more the caching problem ?

The caching problem as I see is such. When Perl_gv_fetchmeth() looks up for 
a name, it traverses the class hierarchy, finds a package that has the
necessary method, and creates a reference to it into the calling class' stash.
That reference is stored into GvCV(hv_fetch(stash,method_name)), and if
present, is then treated as a valid cache entry. It is assigned a
timestamp (or to be more correct, a version stamp) in GvCVGEN, to allow for
quick cache invalidation.

This GvCVGEN flag is also used to distinguish whether a method was
directly declared in the package, or its presence in the package is a
mere cache entry. When we have a package A inherited from B, this mechanism
allows calls to B::foo() to throw "Undefined subroutine &B::foo called", meanwhile
B->foo() succeeds and caches an existing A::foo in B's stash. If then
package B imports "foo" from package C, then the mechanism works exactly
as if B::foo was declared directly, except that the corresponding GV
has GvIMPORTED flag set on.

The problem arises when we save the resolved method in the stash as
a cache entry, and when we have a imported name there too. 
Consider the code:

 package A;
 use base 'B';
 use C qw(moo);
 mro::method_import('A', 0);
 bless({})-> moo;
 moo();

The last line gives exactly "Undefined subroutine &A::foo called",
and it is right because first call to A->moo finds that it needs B::moo,
so a new reference to B::moo is now stored as a cache entry in A::moo slot.
When moo() is then resolved to A::moo(), pp_entersub is careful to
check that its GV doesn't have GvCVGEN flag, which however it now does
after the cache slot was populated. Also, the entry doesn't point to
C::moo anymore, but to B::moo.

Now, I thought that there's no need to change that behavior, so
I though that possibly the caching for these methods should go away instead.
And indeed, all possible tests I could devise, pass just fine, however
at the expense of absence of caching. So basically this is the problem. And
this is why I thought that a GV need two slots for CVs: one for
native and imported names, another for the cached methods.
It seemed ugly to me though, so I thought that possibly instead
an object could have two stashes. These two, by default, would point
to the same structure, and only when we detect that either names
clash, or the object is asked to store a new sub ( not unlike a package 
is asked by *foo = sub ... ), then and only then the structures fork,
and method lookup uses the seconds stash for caching, and the imported
names continue their own way with the first.

Does all this make sense?

-- 
Sincerely,
	Dmitry Karasik


Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About