0

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?

Community
  • 1
  • 1
Zaid
  • 36,680
  • 16
  • 86
  • 155

1 Answers1

1

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 to BEGIN { require Foo; Foo->import if Foo->can(import); }.

Since import is just a subroutine with a special name, the only limitation is the twisted imagination of J. Random Hacker. import can 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 to BEGIN { 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_sub where some_sub does not exist and an AUTOLOAD function is defined in Foo, some_sub would be handled by the AUTOLOAD function. There is a special case for import that exempts it from AUTOLOAD checks. See perlsub on Autoloading.

After AUTOLOAD has (or really has not) been checked, we check inheritance. The same check for matching functions or an AUTOLOAD function 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--@ISA is checked in left to right, depth first order. All this time the AUTOLOAD exception is in place and it will be skipped for import.

Eventually, if no matching method is found, we fall back on UNIVERSAL the universal base class. If the function exists in UNIVERSAL, 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::import is called to handle the job. So, what does this function do?

In Perl 5.12.2 the code looks like this:

sub import {
    return unless $_[0] eq __PACKAGE__;
    return unless @_ > 1;
    require warnings;
    warnings::warnif(
      'deprecated',
      'UNIVERSAL->import is deprecated and will be removed in a future perl',
    );
    goto &Exporter::import;
}

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->can which generally falls back to UNIVERSAL::can. In no place is can called in the normal chain of events. This gets especially hairy when one considers the issues with can in Perl.

  • can may be overridden or reimplemented by any class in the inheritance graph. Which can gets called is subject to method resolution order. All the problems with multiple inheritance apply here.
  • can does 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 overload can to 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 module NEXT to 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.

daotoad
  • 26,689
  • 7
  • 59
  • 100
  • 2
    There is no need to check can for import, UNIVERSAL's import (which does nothing unless called on UNIVERSAL) would be called if there is no import sub in Foo – MkV Nov 27 '10 at 11:43
  • @MkV, `UNIVERSAL::import` checks to see if it is being called by on `UNIVERSAL` and exports `isa`, `can` or `VERSION` as requested--using `UNIVERSAL::import` in this way is deprecated in 5.12. Otherwise it does nothing. So, adding the above behavior in UNIVERSAL::import to the special exception for `import` with `AUTOLOAD` (see perlsub) the example code I gave is correct. – daotoad Nov 27 '10 at 18:11