Possible Duplicate:
In Perl, how can I check from which module a given function was imported?
I’m working with some legacy code where more than one (!) XML-parsing module has been used.
This means that methods with the same name are trampling on one another.
Is there an easy way to tell which package a particular method belongs to?
The simple, and most likely correct answer is that the last module loaded that exports a given function will be the one whose function is used.
When a function (or other symbol) is exported, the symbol table for the receiving package is modified. So if two import functions make changes to the table, the last change is what is preserved.
Recall that
use Foo;is more or less equivalent toBEGIN { require Foo; Foo->import if Foo->can(import); }.Since
importis just a subroutine with a special name, the only limitation is the twisted imagination of J. Random Hacker.importcan be any code. It can do anything or nothing. For example, it could contain logic to make sure no already defined functions are over-written.But, barring any weirdness, the last loaded will be the one in use.
To be 100% technically correct,
use Foo;is equivalent toBEGIN { require Foo; Foo->import; }.Except that this code doesn’t work like you might expect.
Where the exact code varies from my example.
What the above code does is tied up in how method resolution works in Perl.
Ordinarily, a call to
Foo->some_subwheresome_subdoes not exist and anAUTOLOADfunction is defined inFoo,some_subwould be handled by theAUTOLOADfunction. There is a special case forimportthat exempts it fromAUTOLOADchecks. See perlsub on Autoloading.After
AUTOLOADhas (or really has not) been checked, we check inheritance. The same check for matching functions or anAUTOLOADfunction is repeated for each item in the original package’s@ISA. This is a recursive process that includes the parent’s parents and so forth until the entire inheritance tree is checked–@ISAis checked in left to right, depth first order. All this time theAUTOLOADexception is in place and it will be skipped forimport.Eventually, if no matching method is found, we fall back on
UNIVERSALthe universal base class. If the function exists inUNIVERSAL, it is called, otherwise we throw an exception that says the function couldn’t be found.So, in the case of our
Foo->import;call,UNIVERSAL::importis called to handle the job. So, what does this function do?In Perl 5.12.2 the code looks like this:
Note that the first thing the function does is bail out if it wasn’t called on
UNIVERSAL.Now everything I’ve said is true, as long as you don’t do several things:
override the method resolution order. If you do this then method resolution will happen however you have defined it to. Whether we get to UNIVERSAL or not or when is totally up in the air and subject to your whims.
override
UNIVERSAL::import. You could monkey patch UNIVERSAL::import to do whatever you want. Again how this will behave is completely subject to your whims.So, the semi-equivalent code I gave above is a just shorthand for what happens. I thought it would be easier to understand, since it does not require knowing as many details of how Perl does things, but it isn’t 100% equivalent to what really happens. Doing unexpected things breaks the equivalence.
Where my code varies from exact equivalent code
Further, my code calls
Foo->canwhich generally falls back toUNIVERSAL::can. In no place iscancalled in the normal chain of events. This gets especially hairy when one considers the issues withcanin Perl.canmay be overridden or reimplemented by any class in the inheritance graph. Whichcangets called is subject to method resolution order. All the problems with multiple inheritance apply here.candoes not see autoloaded functions. Since autoloading doesn’t apply to import this may not seem like a big deal. The problem is that it is considered good practice to overloadcanto take this into account if you use autoloading. So, this compounds the issues above. The best thing to do is to use the non-core moduleNEXTto enable method redispatch, so that can can be handled by each module in the chain. Unfortunately, this is rarely done.Conclusion
All this is one hell of a lot to chew on.
You can accept my shorthand, knowing that in some cases, it is not exactly correct.
Or you can accept the actual code, that has its own set of exceptions.
Either way, if you break through the surface of either example there are some subtle issues to cope with.