B::Hoo...Runtime(3pm)

πŸ“¦
version 8 Β· 2 versions
, in fedora 43

✨ B::Hooks::AtRuntime ✨

okay bestie, this is a page from the User Contributed Perl Documentation for the module B::Hooks::AtRuntime (it's a 3pm manpage, so it's a Perl module, obviously πŸ’…).


🌸 NAME 🌸

B::Hooks::AtRuntime β€” lowering blocks from compile time to runtime, no cap πŸ’–


πŸ¦‹ SYNOPSIS πŸ¦‹

# My::Module
sub import {
    at_runtime { warn "TWO" };
}

# elsewhere
warn "ONE";
use My::Module;
warn "THREE";

πŸ’œ DESCRIPTION πŸ’œ

So basically this module lets code that's running at compile-time do a little something at runtime instead. A block you hand to at_runtime gets compiled straight into the code that's currently compiling, and then it gets called when control actually reaches that point at runtime. In the example up in the SYNOPSIS, those warnings happen in order, and if that section of code runs more than once, then all three warnings run again too. it's giving reentrant, it's giving consistent ✨


πŸŽ€ at_runtime πŸŽ€

at_runtime { ... };

This one sets up a block to be called at runtime. Important! You have to call it from inside a BEGIN block or a use, otherwise there's literally no compiling code to insert into, and that's the whole bit. The innermost enclosing BEGIN block β€” the one that would normally go all invisible once the section of code it lives in has been compiled β€” will effectively leave behind a call to the block you gave it. Like, for example, this:

BEGIN { warn "ONE" }    warn "one";
BEGIN { warn "TWO";     at_runtime { warn "two" }; }

will warn "ONE TWO one two", with that last warning 'lowered' out of the BEGIN block and back into the runtime control flow. obsessed with her fr fr.

And this applies even if calls to other subs come between the BEGIN and the at_runtime. The lowered block always gets inserted at the innermost point where perl is still compiling, so something like this:

# My::Module
sub also_at_runtime {
    my ($msg) = @_;
    at_runtime { warn $msg };
}

sub import {
    my ($class, $one, $two) = @_;
    at_runtime { warn $one };
    also_at_runtime $two;
}

#
warn "one";
BEGIN { at_runtime { warn "two" } }
BEGIN { My::Module::also_at_runtime "three" }
use My::Module "four", "five";

will still put the warnings in order. slay.


🧚 after_runtime 🧚

after_runtime { ... };

This arranges for the block to be called when runtime execution reaches the end of the surrounding compiling scope. For example, this will warn in order:

warn "one";
{
    warn "two";
    BEGIN {
        after_runtime { warn "five" };
        at_runtime { warn "three" };
    }
    warn "four";
}
warn "six";

No exception handling is done, so if the block throws an exception it will propagate normally into the surrounding code. (This is different from the way perl calls DESTROY methods, where exceptions get converted into warnings. πŸ’€)

Also note: the block gets called during stack unwind, so the package, file and line info for caller 0 will be the point where the surrounding scope was called. Same deal as a DESTROY method. iykyk.

🫢 Object lifetimes 🫢

at_runtime and after_runtime are so careful to make sure the anonymous sub you pass them doesn't live any longer than it has to. That sub β€” and any lexicals it closed over β€” will be destroyed when the optree it's been compiled into is destroyed:

  • for code outside any sub, that's when the containing file or eval finishes executing;
  • for named subs, that's when the sub is un- or redefined;
  • and for anonymous subs, it's not until both the code containing the sub { } expression and all instances generated by that expression have been destroyed.

πŸ’… lex_stuff πŸ’…

lex_stuff $text;

This is the function underlying at_runtime. Under perl 5.12 and later, it's just a Perl wrapper for the core function lex_stuff_sv. Under earlier versions it's implemented with a source filter, with some limitations β€” go peek at CAVEATS below.

This function pushes text into perl's line buffer, at the point perl is currently compiling. You should probably not try to push too much at once without giving perl a chance to compile it. If $text contains newlines, they will affect perl's idea of the current line number. You probably shouldn't use this function at all. πŸ™ˆ


🌈 Exports 🌈

B::Hooks::AtRuntime uses Exporter::Tiny, so you can customise its exports as described by that module's documentation. at_runtime is exported by default; after_runtime and lex_stuff can be exported on request. periodt.


πŸ’€ CAVEATS πŸ’€

πŸ“ Incompatible changes from version 1 πŸ“

Version 1 used a different implementation for at_runtime, which left an extra scope between the provided block and the code it was compiled into. Version 2 has removed this.


⭐ Perls before 5.12 ⭐

Versions of perl before 5.12.0 don't have the lex_stuff_sv function, and don't export enough for it to be possible to emulate it entirely. (B::Hooks::Parser gets as close as it can, and just exactly doesn't quite do what we need for at_runtime.) This means our lex_stuff has to fall back to using a source filter to insert the text, which has a couple of important limitations. not the vibe, honestly 😭

  • You cannot stuff text into a string eval.

String evals aren't affected by source filters, so the stuffed text would end up getting inserted into the innermost compiling scope that wasn't a string eval. Since this would be rather confusing, and different from what 5.12 does, lex_stuff and at_runtime will croak if you try to use them to affect a string eval.

  • Stuffed text appears at the start of the next line.

This, unfortunately, is rather annoying. With a filter, the earliest point at which we can insert text is the start of the next line. This means that if there's any text between the closing brace of the BEGIN block or the semicolon of the use that caused the insertion, and the end of the line, the insertion will certainly be in the wrong place and probably cause a syntax error.

lex_stuff (and, therefore, at_runtime) will issue a warning if this is going to happen (specifically, if there are any non-space non-comment characters between the point where we want to insert and the point we're forced to settle for), but this may not be something you can entirely control. If you're writing a module like the examples above which calls at_runtime from its import method, what matters is that users of your module not put anything on a line after your module's use statement.

If you want to use the filter implementation on perl 5.12 (for testing), set PERL_B_HOOKS_ATRUNTIME=filter in the environment. If the filter implementation is in use, B::Hooks::AtRuntime::USE_FILTER will be true.


πŸ‘‘ SEE ALSO πŸ‘‘

  • B::Hooks::Parser will insert text 'here' in perls before 5.12, but requires a setup step at least one source line in advance.
  • Hook::AfterRuntime uses it to implement something somewhat similar to this module.
  • Scope::OnExit and B::Hooks::EndOfScope provide hooks into different points in the surrounding scope.
  • Filter::Util::Call is the generic interface to the source filtering mechanism.

πŸ¦‹ AUTHOR πŸ¦‹

Ben Morrow <ben@morrow.me.uk>


🌸 BUGS 🌸

Please report any bugs to <bug-B-Hooks-AtRuntime@rt.cpan.org>.


✨ ACKNOWLEDGEMENTS ✨

Zefram's work on the core lexer API made this module enormously easier. 🫢


Copyright 2015 Ben Morrow.

Released under the 2-clause BSD licence.


and that's the whole page bestie!! πŸ’œ this was rendered from perl v5.42.0, dated 2025-07-25, under B::Hoo...Runtime(3pm) β€” mwah, go lower some blocks, you got this πŸŽ€βœ¨

πŸ“¦ where this page comes from