Sub::Uplevel(3)

✨ Sub::Uplevel ✨

Okay so bestie, welcome to the Sub::Uplevel(3) page from the User Contributed Perl Documentation β€” this is the manual where Perl gets to pretend a function is living its best life in a higher stack frame. πŸ’…


🌸 NAME 🌸

Sub::Uplevel β€” apparently run a function in a higher stack frame. No cap, that’s the whole vibe.


πŸŽ€ VERSION πŸŽ€

This glow-up is version 0.2800, periodt. πŸ’–


πŸ¦‹ SYNOPSIS πŸ¦‹

  use Sub::Uplevel;

  sub foo {
      print join " - ", caller;
  }

  sub bar {
      uplevel 1, \&foo;
  }

  #line 11
  bar();    # main - foo.plx - 11

🌈 DESCRIPTION 🌈

Like Tcl's uplevel() function, but not quite so dangerous. The idea is just to fool caller(). All the really naughty bits of Tcl's uplevel() are avoided. 😌

🚨 THIS IS NOT THE SORT OF THING YOU WANT TO DO EVERYDAY 🚨

πŸŒ€ uplevel πŸŒ€

uplevel $num_frames, \&func, @args;

Makes the given function think it's being executed $num_frames higher than the current stack level. So when they use caller($frames) it will actually give caller($frames + $num_frames) for them. Sneaky but cute. 🫢

"uplevel(1, \&some_func, @_)" is effectively "goto &some_func" but you don't immediately exit the current subroutine. So while you can't do this:

sub wrapper {
    print "Before\n";
    goto &some_func;
    print "After\n";
}

you can do this:

sub wrapper {
    print "Before\n";
    my @out = uplevel 1, &some_func;
    print "After\n";
    return @out;
}

uplevel has the ability to issue a warning if $num_frames is more than the current call stack depth, although this warning is disabled and compiled out by default as the check is relatively expensive. Lowkey a mood. πŸ’Έ

To enable the check for debugging or testing, you should set the global $Sub::Uplevel::CHECK_FRAMES to true before loading Sub::Uplevel for the first time as follows:

#!/usr/bin/perl

BEGIN {
    $Sub::Uplevel::CHECK_FRAMES = 1;
}
use Sub::Uplevel;

Setting or changing the global after the module has been loaded will have no effect. Nope, not even a little. πŸ™ˆ


🌟 EXAMPLE 🌟

The main reason I wrote this module is so I could write wrappers around functions and they wouldn't be aware they've been wrapped. Main character energy for real. πŸ‘‘

use Sub::Uplevel;

my $original_foo = \&foo;

*foo = sub {
    my @output = uplevel 1, $original_foo;
    print "foo() returned:  @output";
    return @output;
};

If this code frightens you you should not use this module. That’s the whole warning, bestie. πŸ’€


πŸ’€ BUGS and CAVEATS πŸ’€

Well, the bad news is uplevel() is about 5 times slower than a normal function call. XS implementation anyone? It also slows down every invocation of caller(), regardless of whether uplevel() is in effect. 😭

Sub::Uplevel overrides CORE::GLOBAL::caller temporarily for the scope of each uplevel call. It does its best to work with any previously existing CORE::GLOBAL::caller (both when Sub::Uplevel is first loaded and within each uplevel call) such as from Contextual::Return or Hook::LexWrap. 🧚

However, if you are routinely using multiple modules that override CORE::GLOBAL::caller, you are probably asking for trouble. That’s not a vibe check you want to fail. πŸ™…

You should load Sub::Uplevel as early as possible within your program. As with all CORE::GLOBAL overloading, the overload will not affect modules that have already been compiled prior to the overload. One module that often is unavoidably loaded prior to Sub::Uplevel is Exporter. To forcibly recompile Exporter (and Exporter::Heavy) after loading Sub::Uplevel, use it with the ":aggressive" tag:

use Sub::Uplevel qw/:aggressive/;

The private function Sub::Uplevel::_force_reload() may be passed a list of additional modules to reload if ":aggressive" is not aggressive enough. Reloading modules may break things, so only use this as a last resort. Like, actual last resort. 🫠

As of version 0.20, Sub::Uplevel requires Perl 5.6 or greater.


πŸ¦‹ HISTORY πŸ¦‹

Those who do not learn from HISTORY are doomed to repeat it. πŸ“š

The lesson here is simple: Don't sit next to a Tcl programmer at the dinner table. πŸ’…


🫢 THANKS 🫢

Thanks to Brent Welch, Damian Conway and Robin Houston. Absolute icons. 🌈

See http://www.perl.com/perl/misc/Artistic.html


πŸ‘€ SEE ALSO πŸ‘€

PadWalker (for the similar idea with lexicals), Hook::LexWrap, Tcl's uplevel() at http://www.scriptics.com/man/tcl8.4/TclCmd/uplevel.htm


πŸ’– SUPPORT πŸ’–

🐞 Bugs / Feature Requests 🐞

Please report any bugs or feature requests through the issue tracker at <https://github.com/Perl-Toolchain-Gang/Sub-Uplevel/issues>. You will be notified automatically of any progress on your issue. πŸ’Œ

πŸ“¦ Source Code πŸ“¦

This is open source software. The code repository is available for public review and contribution under the terms of the license. 🫢

<https://github.com/Perl-Toolchain-Gang/Sub-Uplevel>

git clone https://github.com/Perl-Toolchain-Gang/Sub-Uplevel.git

🌟 AUTHORS 🌟

  • Michael Schwern β€” <mschwern@cpan.org>
  • David Golden β€” <dagolden@cpan.org>

πŸŽ‰ CONTRIBUTORS πŸŽ‰

  • Adam Kennedy β€” <adamk@cpan.org>
  • Alexandr Ciornii β€” <alexchorny@gmail.com>
  • David Golden β€” <xdg@xdg.me>
  • Graham Ollis β€” <plicease@cpan.org>
  • J. Nick Koston β€” <nick@cpanel.net>
  • Michael Gray β€” <mg13@sanger.ac.uk>

This software is copyright (c) 2017 by Michael Schwern and David Golden.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.


πŸ’– That’s the whole Sub::Uplevel(3) manual, bestie β€” source/version perl v5.42.0, dated 2018-09-14. Mwah! πŸ’…βœ¨

πŸ“¦ where this page comes from