Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW

Overriding caller everywhere

by Sartak (Hermit)
on Aug 06, 2007 at 04:19 UTC ( [id://630760] : perlquestion . print w/replies, xml ) Need Help??

Sartak has asked for the wisdom of the Perl Monks concerning the following question:


perlsub mentions that you can redefine CORE::GLOBAL::builtin and it should redefine that builtin globally (i.e. in all packages).

I'm trying to do this for caller and it isn't working. Here's a test script that demonstrates the problem. Note that glob is successfully redefined.

#!perl use strict; use warnings; use Test::More tests => 2; # hide package Outer (a little stupidly) sub my_caller { my $package = CORE::caller(@_); $package = 'main' if $package eq 'Outer'; return $package; } # redefine both caller (which fails) and glob (which works) *CORE::GLOBAL::caller = \&my_caller; *CORE::GLOBAL::glob = \&my_caller; # actual tests my ($got_caller, $got_glob); Outer::outer(); is($got_caller, 'main', 'redefined caller in all packages'); is($got_glob, 'main', 'redefined glob in all packages'); { package Inner; sub inner { $got_caller = caller(); $got_glob = glob(); } } { package Outer; sub outer { Inner::inner() } } __END__ 1..2 Name "CORE::GLOBAL::caller" used only once: possible typo at test.t li +ne 14. Subroutine CORE::GLOBAL::glob redefined at test.t line 15. not ok 1 - redefined caller in all packages # Failed test 'redefined caller in all packages' # at test.t line 20. # got: 'Outer' # expected: 'main' ok 2 - redefined glob in all packages # Looks like you failed 1 test of 2.

Overriding CORE::GLOBAL::caller seems to work if you do it with dynamic scope (such as in Sub::Uplevel), or it only works within the given package (such as in Hook::LexWrap).

If there's no way to override caller globally, then.. why isn't there? It would come in handy for one of my modules.

Thanks for any advice.

Replies are listed 'Best First'.
Re: Overriding caller everywhere (BEGIN)
by tye (Sage) on Aug 06, 2007 at 05:02 UTC

    Your test code that calls caller() has already been compiled before you override anything. Doing the overriding in a BEGIN block is one way to fix that.

    - tye        

      Doing the overriding in a BEGIN block is one way to fix that.

      And, to be clear for the OP, when you use a module like Sub::Uplevel or Hook::LexWrap (or Contextual::Return), the compilation of those modules happens immediately as a BEGIN block (c.f. docs for use), thus caller is replaced globally before the rest of your code compiles.

      use Sub::Uplevel; # *CORE::GLOBAL::caller replaced here print caller(); # calls the replacement


      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

      Doing it in a BEGIN block does seem to work in this example. But why does the identical glob test work without BEGIN? :)

        Overriding of glob is special-cased. Instead of changing things so that Perl code is compiled to call your replacement routine "directly", the built-in &CORE::glob is still called and it notices (at run-time) the override and calls your replacement routine (and passes an extra argument that provides the context so that your replacement can correctly handle repeated calls made in scalar context). So you can't use CORE::glob("foo") to bypass this override (and you can't change the prototype of glob like you can with other overridden built-ins when using a recent version of Perl).

        - tye        

Re: Overriding caller everywhere
by Anonymous Monk on Aug 06, 2007 at 05:33 UTC
    Not all core functions are overridable

      caller is though; use prototype to see if you can override a builtin.