Skip to content

Commit 72af433

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 b0bc042 commit 72af433

File tree

11 files changed

+165
-3
lines changed

11 files changed

+165
-3
lines changed

MANIFEST

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6026,6 +6026,7 @@ t/op/repeat.t See if x operator works
60266026
t/op/require_37033.t See if require always closes rsfp
60276027
t/op/require_errors.t See if errors from require are reported correctly
60286028
t/op/require_gh20577.t Make sure updating %INC from an INC hook doesnt break @INC
6029+
t/op/require_hook.t See if require hooks work properly.
60296030
t/op/require_override.t See if require handles no argument properly
60306031
t/op/reset.t See if reset operator works
60316032
t/op/reverse.t See if reverse operator works

embedvar.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

intrpvar.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,7 @@ PERLVAR(I, origfilename, char *)
495495
PERLVARI(I, xsubfilename, const char *, NULL)
496496
PERLVAR(I, diehook, SV *)
497497
PERLVAR(I, warnhook, SV *)
498+
PERLVARI(I, requirehook, SV *,NULL)
498499

499500
/* switches */
500501
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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,8 @@ perl_destruct(pTHXx)
938938
PL_warnhook = NULL;
939939
SvREFCNT_dec(PL_diehook);
940940
PL_diehook = NULL;
941+
SvREFCNT_dec(PL_requirehook);
942+
PL_requirehook = NULL;
941943

942944
/* call exit list functions */
943945
while (PL_exitlistlen-- > 0)

pod/perldiag.pod

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

2173+
=item $SIG{__REQUIRE__} may only be a CODE reference or undef
2174+
2175+
(F) You attempted to assign something other than undef or a CODE ref to
2176+
C<$SIG{__REQUIRE__}>, require hooks only allow CODE refs.
2177+
See L<perlfunc/require> for more details.
2178+
21732179
=item entering effective %s failed
21742180

21752181
(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+
(void)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
@@ -15824,6 +15824,7 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
1582415824
PL_xsubfilename = proto_perl->Ixsubfilename;
1582515825
PL_diehook = sv_dup_inc(proto_perl->Idiehook, param);
1582615826
PL_warnhook = sv_dup_inc(proto_perl->Iwarnhook, param);
15827+
PL_requirehook = sv_dup_inc(proto_perl->Irequirehook, param);
1582715828

1582815829
/* switches */
1582915830
PL_patchlevel = sv_dup_inc(proto_perl->Ipatchlevel, param);

0 commit comments

Comments
 (0)