Skip to content

Commit 34e94e1

Browse files
committed
pp_ctl.c - add support for a __REQUIRE__
Hook is called before anything else happens inside of a require, and may return a callback which will be called after everything else is done.
1 parent 702e757 commit 34e94e1

File tree

10 files changed

+166
-3
lines changed

10 files changed

+166
-3
lines changed

MANIFEST

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6008,6 +6008,7 @@ t/op/repeat.t See if x operator works
60086008
t/op/require_37033.t See if require always closes rsfp
60096009
t/op/require_errors.t See if errors from require are reported correctly
60106010
t/op/require_gh20577.t Make sure updating %INC from an INC hook doesnt break @INC
6011+
t/op/require_hook.t See if require hooks work properly.
60116012
t/op/require_override.t See if require handles no argument properly
60126013
t/op/reset.t See if reset operator works
60136014
t/op/reverse.t See if reverse operator works

intrpvar.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,7 @@ PERLVAR(I, origfilename, char *)
492492
PERLVARI(I, xsubfilename, const char *, NULL)
493493
PERLVAR(I, diehook, SV *)
494494
PERLVAR(I, warnhook, SV *)
495+
PERLVARI(I, requirehook, SV *,NULL)
495496

496497
/* switches */
497498
PERLVAR(I, patchlevel, SV *)

mg.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1670,15 +1670,20 @@ Perl_magic_setsig(pTHX_ SV *sv, MAGIC *mg)
16701670
if (*s == '_') {
16711671
if (memEQs(s, len, "__DIE__"))
16721672
svp = &PL_diehook;
1673-
else if (memEQs(s, len, "__WARN__")
1673+
else if (memEQs(s, len, "__REQUIRE__")) {
1674+
if (sv && SvOK(sv) && (!SvROK(sv) || SvTYPE(SvRV(sv))!= SVt_PVCV))
1675+
croak("$SIG{__REQUIRE__} may only be a CODE reference or undef");
1676+
svp = &PL_requirehook;
1677+
} else if (memEQs(s, len, "__WARN__")
16741678
&& (sv ? 1 : PL_warnhook != PERL_WARNHOOK_FATAL)) {
16751679
/* Merge the existing behaviours, which are as follows:
16761680
magic_setsig, we always set svp to &PL_warnhook
16771681
(hence we always change the warnings handler)
16781682
For magic_clearsig, we don't change the warnings handler if it's
16791683
set to the &PL_warnhook. */
16801684
svp = &PL_warnhook;
1681-
} else if (sv) {
1685+
}
1686+
else if (sv) {
16821687
SV *tmp = sv_newmortal();
16831688
Perl_croak(aTHX_ "No such hook: %s",
16841689
pv_pretty(tmp, s, len, 0, NULL, NULL, 0));
@@ -1750,8 +1755,9 @@ Perl_magic_setsig(pTHX_ SV *sv, MAGIC *mg)
17501755
if (i) {
17511756
(void)rsignal(i, PL_csighandlerp);
17521757
}
1753-
else
1758+
else {
17541759
*svp = SvREFCNT_inc_simple_NN(sv);
1760+
}
17551761
} else {
17561762
if (sv && SvOK(sv)) {
17571763
s = SvPV_force(sv, len);

perl.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,10 @@ perl_destruct(pTHXx)
938938
PL_warnhook = NULL;
939939
SvREFCNT_dec(PL_diehook);
940940
PL_diehook = NULL;
941+
if (PL_requirehook) {
942+
SvREFCNT_dec(PL_requirehook);
943+
PL_requirehook = NULL;
944+
}
941945

942946
/* call exit list functions */
943947
while (PL_exitlistlen-- > 0)

pod/perldiag.pod

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2162,6 +2162,12 @@ the C<encoding> pragma, is no longer supported as of Perl 5.26.0.
21622162
Setting it to anything other than C<undef> is a fatal error as of Perl
21632163
5.28.
21642164

2165+
=item $SIG{__REQUIRE__} may only be a CODE reference or undef
2166+
2167+
(F) You attempted to assign something other than undef or a CODE ref to
2168+
C<$SIG{__REQUIRE__}>, require hooks only allow CODE refs.
2169+
See L<perlfunc/require> for more details.
2170+
21652171
=item entering effective %s failed
21662172

21672173
(F) While under the C<use filetest> pragma, switching the real and

pod/perlfunc.pod

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6969,6 +6969,55 @@ require executes at all.
69696969

69706970
As of 5.37.7 C<@INC> values of undef will be silently ignored.
69716971

6972+
The function C<require()> is difficult to wrap properly. Many modules consult
6973+
the stack to find information about their caller, and injecting a new stack
6974+
frame by wrapping C<require()> often breaks things. Nevertheless it can be
6975+
very helpful to have the ability to perform actions before and after a
6976+
C<require>, for instance for trace utilities like C<Devel::TraceUse> or to
6977+
measure time to load and memory consumption of the require graph. Because of
6978+
the difficulties in safely creating a C<require()> wrapper in 5.37.7 we
6979+
introduced a new mechanism.
6980+
6981+
As of 5.37.7, prior to any other actions it performs, C<require> will check if
6982+
C<$SIG{__REQUIRE__}> contains a coderef, and if it does it will be called with
6983+
the filename form of the item being loaded. This function call is informative
6984+
only, and will not affect the require process except by throwing a fatal
6985+
exception, which will be treated as though the required code itself had thrown
6986+
an exception. However the function may return a code reference, which will be
6987+
executed in an eval with no arguments after the require completes, regardless
6988+
of how the compilation completed even if the require throws a fatal exception.
6989+
The function must consult C<%INC> to determine if the require failed or not.
6990+
For instance the following code will print some diagnostics before and after
6991+
every C<require> statement. The example also includes logic to chain the
6992+
signal, so that multiple signals can cooperate. Well behaved
6993+
C<$SIG{__REQUIRE__}> handlers should always take this into account.
6994+
6995+
{
6996+
my $old_hook = $SIG{__REQUIRE__};
6997+
local $SIG{__REQUIRE__} = sub {
6998+
my ($name) = @_;
6999+
my $old_hook_ret;
7000+
$old_hook_ret = $old_hook->($name) if $old_hook;
7001+
warn "Requiring: $name\n";
7002+
return sub {
7003+
$old_hook_ret->() if $old_hook_ret;
7004+
warn sprintf "Finished requiring %s: %s\n",
7005+
$name, $INC{$name} ? "loaded" :"failed";
7006+
};
7007+
};
7008+
require Whatever;
7009+
}
7010+
7011+
This hook executes for ALL C<require> statements, unlike C<INC> and C<INCDIR>
7012+
hooks, which are only executed for relative file names, and it executes first
7013+
before any other special behaviour inside of require. Note that the initial hook
7014+
in C<$SIG{__REQUIRE__}> is *not* executed inside of an eval, and throwing an
7015+
exception will stop further processing, but the after hook it may return is
7016+
executed inside of an eval, and any exceptions it throws will be silently ignored.
7017+
This is because it executes inside of the scope cleanup logic that is triggered
7018+
after the require completes, and an exception at this time would not stop the
7019+
module from being loaded, etc.
7020+
69727021
For a yet-more-powerful import facility, see
69737022
L<C<use>|/use Module VERSION LIST> and L<perlmod>.
69747023

pod/perlvar.pod

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,13 @@ and use an C<END{}> or CORE::GLOBAL::die override instead.
779779
See L<perlfunc/die>, L<perlfunc/warn>, L<perlfunc/eval>, and
780780
L<warnings> for additional information.
781781

782+
The routine indicated by C<$SIG{__REQUIRE__}> is called by C<require>
783+
before it looks up C<@INC>, or compiles any code. It is called
784+
with a single argument, the filename for the item being required (package
785+
names are converted to paths). It may return a coderef which will be
786+
executed when the C<require> completes, either via exception or via
787+
completion of the require statement.
788+
782789
=item $BASETIME
783790

784791
=item $^T

pp_ctl.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4089,6 +4089,19 @@ S_require_version(pTHX_ SV *sv)
40894089
RETPUSHYES;
40904090
}
40914091

4092+
static void
4093+
S_call_post_require_hook(pTHX_ SV *hook_sv) {
4094+
dSP;
4095+
sv_2mortal(hook_sv);
4096+
ENTER_with_name("call_POST_REQUIRE");
4097+
SAVETMPS;
4098+
PUSHMARK(SP);
4099+
int count = call_sv(hook_sv, G_VOID);
4100+
FREETMPS;
4101+
LEAVE_with_name("call_POST_REQUIRE");
4102+
}
4103+
4104+
40924105
/* Handle C<require Foo::Bar>, C<require "Foo/Bar.pm"> and C<do "Foo.pm">.
40934106
* The first form will have already been converted at compile time to
40944107
* the second form */
@@ -4243,6 +4256,27 @@ S_require_file(pTHX_ SV *sv)
42434256

42444257
PERL_DTRACE_PROBE_FILE_LOADING(unixname);
42454258

4259+
SV *after_requirehook_sv = NULL;
4260+
if (PL_requirehook && SvROK(PL_requirehook) && SvTYPE(SvRV(PL_requirehook)) == SVt_PVCV) {
4261+
SV* name_sv = sv_mortalcopy(sv);
4262+
4263+
ENTER_with_name("call_PRE_REQUIRE");
4264+
SAVETMPS;
4265+
EXTEND(SP, 1);
4266+
PUSHMARK(SP);
4267+
PUSHs(name_sv); /* always use the object for method calls */
4268+
PUTBACK;
4269+
int count = call_sv(PL_requirehook, G_SCALAR);
4270+
SPAGAIN;
4271+
if (count && SvOK(*SP) && SvROK(*SP) && SvTYPE(SvRV(*SP)) == SVt_PVCV)
4272+
after_requirehook_sv = SvREFCNT_inc(*SP);
4273+
FREETMPS;
4274+
LEAVE_with_name("call_PRE_REQUIRE");
4275+
if (after_requirehook_sv)
4276+
SAVEDESTRUCTOR_X(S_call_post_require_hook, after_requirehook_sv);
4277+
}
4278+
4279+
42464280
/* Try to locate and open a file, possibly using @INC */
42474281

42484282
/* with "/foo/bar.pm", "./foo.pm" and "../foo/bar.pm", try to load

sv.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15811,6 +15811,7 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
1581115811
PL_xsubfilename = proto_perl->Ixsubfilename;
1581215812
PL_diehook = sv_dup_inc(proto_perl->Idiehook, param);
1581315813
PL_warnhook = sv_dup_inc(proto_perl->Iwarnhook, param);
15814+
PL_requirehook = sv_dup_inc(proto_perl->Irequirehook, param);
1581415815

1581515816
/* switches */
1581615817
PL_patchlevel = sv_dup_inc(proto_perl->Ipatchlevel, param);

t/op/require_hook.t

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!perl
2+
3+
BEGIN {
4+
chdir 't' if -d 't';
5+
require './test.pl';
6+
set_up_inc( qw(../lib) );
7+
}
8+
9+
use strict;
10+
use warnings;
11+
12+
plan(tests => 4);
13+
14+
# Dedupe @INC. In a future patch we /may/ refuse to process items
15+
# more than once and deduping here will prevent the tests from failing
16+
# should we make that change.
17+
my %seen; @INC = grep {!$seen{$_}++} @INC;
18+
{
19+
# as of 5.37.7
20+
fresh_perl_like(
21+
'$SIG{__REQUIRE__} = "x";',
22+
qr!\$SIG\{__REQUIRE__\} may only be a CODE reference or undef!,
23+
{ }, '$SIG{__REQUIRE__} forbids non code refs (string)');
24+
}
25+
{
26+
# as of 5.37.7
27+
fresh_perl_like(
28+
'$SIG{__REQUIRE__} = [];',
29+
qr!\$SIG\{__REQUIRE__\} may only be a CODE reference or undef!,
30+
{ }, '$SIG{__REQUIRE__} forbids non code refs (array ref)');
31+
}
32+
{
33+
# as of 5.37.7
34+
fresh_perl_like(
35+
'$SIG{__REQUIRE__} = sub { die "Not allowed to load $_[0]" }; require Frobnitz;',
36+
qr!Not allowed to load Frobnitz\.pm!,
37+
{ }, '$SIG{__REQUIRE__} exceptions stop require');
38+
}
39+
{
40+
# as of 5.37.7
41+
fresh_perl_is(
42+
'use lib "./lib/caller"; '.
43+
'$SIG{__REQUIRE__} = sub { my ($name)= @_; warn "before $name"; '.
44+
'return sub { warn "after $name" } }; require Apack;',
45+
<<'EOF_WANT',
46+
before Apack.pm at - line 1.
47+
before Bpack.pm at - line 1.
48+
before Cpack.pm at - line 1.
49+
after Cpack.pm at - line 1.
50+
after Bpack.pm at - line 1.
51+
after Apack.pm at - line 1.
52+
EOF_WANT
53+
{ }, '$SIG{__REQUIRE__} works as expected with t/lib/caller/Apack');
54+
}

0 commit comments

Comments
 (0)