Skip to content

Commit d0b1ad1

Browse files
committed
pp_ctl.c - add support for hooking require.
This defines a new magic hash C<%{^HOOK}> which is intended to be used for hooking keywords. It is similar to %SIG in that the values it contains are validated on set, and it is not allowed to store something in C<%{^HOOK}> that isn't supposed to be there. Hooks are expected to be coderefs (people can use currying if they really want to put an object in there, the API is deliberately simple.) The C<%{^HOOK}> hash is documented to have keys of the form "${keyword}__${phase}" where $phase is either "before" or "after" and in this initial release two hooks are supported, "require__before" and "require__after": The C<require__before> hook is called before require is executed, including any @inc hooks that might be fired. It is called with the path of the file being required, just as would be stored in %INC. The hook may alter the filename by writing to $_[0] and it may return a coderef to be executed *after* the require has completed, otherwise the return is ignored. This coderef is also called with the path of the file which was required, and it will be called regardless as to whether the require (or its dependencies) die during execution. This mechanism makes it trivial and safe to share state between the initial hook and the coderef it returns. The C<require__after> hook is similar to the C<require__before> hook however except that it is called after the require completes (successfully or not), and its return is ignored always.
1 parent 9961aac commit d0b1ad1

25 files changed

+603
-13
lines changed

MANIFEST

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5787,10 +5787,13 @@ t/io/tell.t See if file seeking works
57875787
t/io/through.t See if pipe passes data intact
57885788
t/io/utf8.t See if file seeking works
57895789
t/japh/abigail.t Obscure tests
5790-
t/lib/caller/Apack.pm test Module for caller.t
5791-
t/lib/caller/Bpack.pm test Module for caller.t
5792-
t/lib/caller/Cpack.pm test Module for caller.t
5793-
t/lib/caller/Foo.pm test Module for caller.t
5790+
t/lib/caller/Apack.pm test Module for caller.t and t/op/hook/require.t
5791+
t/lib/caller/Bicycle.pm test Module for t/op/hook/require.t (cyclic)
5792+
t/lib/caller/Bpack.pm test Module for caller.t and t/op/hook/require.t
5793+
t/lib/caller/Cpack.pm test Module for caller.t and t/op/hook/require.t
5794+
t/lib/caller/Cycle.pm test Module for t/op/hook/require.t (cyclic)
5795+
t/lib/caller/Foo.pm test Module for caller.t and t/op/hook/require.t
5796+
t/lib/caller/Tricycle.pm test Module for t/op/hook/require.t (cyclic)
57945797
t/lib/CannotParse.pm For test case in op/require_errors.t
57955798
t/lib/charnames/alias Tests of "use charnames" with aliases.
57965799
t/lib/Cname.pm Test charnames in regexes (op/pat.t)
@@ -6045,6 +6048,7 @@ t/op/hashassign.t See if hash assignments work
60456048
t/op/hashwarn.t See if warnings for bad hash assignments work
60466049
t/op/heredoc.t See if heredoc edge and corner cases work
60476050
t/op/hexfp.t See if hexadecimal float literals work
6051+
t/op/hook/require.t See if require hooks work properly.
60486052
t/op/inc.t See if inc/dec of integers near 32 bit limit work
60496053
t/op/inccode.t See if coderefs work in @INC
60506054
t/op/inccode-tie.t See if tie to @INC works

embed.fnc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1789,6 +1789,11 @@ dp |int |magic_clearhint|NN SV *sv \
17891789
dp |int |magic_clearhints \
17901790
|NN SV *sv \
17911791
|NN MAGIC *mg
1792+
p |int |magic_clearhook|NULLOK SV *sv \
1793+
|NN MAGIC *mg
1794+
p |int |magic_clearhookall \
1795+
|NULLOK SV *sv \
1796+
|NN MAGIC *mg
17921797
p |int |magic_clearisa |NULLOK SV *sv \
17931798
|NN MAGIC *mg
17941799
p |int |magic_clearpack|NN SV *sv \
@@ -1889,6 +1894,11 @@ p |int |magic_setenv |NN SV *sv \
18891894
|NN MAGIC *mg
18901895
dp |int |magic_sethint |NN SV *sv \
18911896
|NN MAGIC *mg
1897+
p |int |magic_sethook |NULLOK SV *sv \
1898+
|NN MAGIC *mg
1899+
p |int |magic_sethookall \
1900+
|NN SV *sv \
1901+
|NN MAGIC *mg
18921902
p |int |magic_setisa |NN SV *sv \
18931903
|NN MAGIC *mg
18941904
p |int |magic_setlvref |NN SV *sv \

embed.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,8 @@
957957
# define magic_clearenv(a,b) Perl_magic_clearenv(aTHX_ a,b)
958958
# define magic_clearhint(a,b) Perl_magic_clearhint(aTHX_ a,b)
959959
# define magic_clearhints(a,b) Perl_magic_clearhints(aTHX_ a,b)
960+
# define magic_clearhook(a,b) Perl_magic_clearhook(aTHX_ a,b)
961+
# define magic_clearhookall(a,b) Perl_magic_clearhookall(aTHX_ a,b)
960962
# define magic_clearisa(a,b) Perl_magic_clearisa(aTHX_ a,b)
961963
# define magic_clearpack(a,b) Perl_magic_clearpack(aTHX_ a,b)
962964
# define magic_clearsig(a,b) Perl_magic_clearsig(aTHX_ a,b)
@@ -992,6 +994,8 @@
992994
# define magic_setdefelem(a,b) Perl_magic_setdefelem(aTHX_ a,b)
993995
# define magic_setenv(a,b) Perl_magic_setenv(aTHX_ a,b)
994996
# define magic_sethint(a,b) Perl_magic_sethint(aTHX_ a,b)
997+
# define magic_sethook(a,b) Perl_magic_sethook(aTHX_ a,b)
998+
# define magic_sethookall(a,b) Perl_magic_sethookall(aTHX_ a,b)
995999
# define magic_setisa(a,b) Perl_magic_setisa(aTHX_ a,b)
9961000
# define magic_setlvref(a,b) Perl_magic_setlvref(aTHX_ a,b)
9971001
# define magic_setmglob(a,b) Perl_magic_setmglob(aTHX_ a,b)

embedvar.h

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

gv.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2219,6 +2219,13 @@ S_gv_magicalize(pTHX_ GV *gv, HV *stash, const char *name, STRLEN len,
22192219
if (memEQs(name, len, "\007LOBAL_PHASE"))
22202220
goto ro_magicalize;
22212221
break;
2222+
case '\010': /* %{^HOOK} */
2223+
if (memEQs(name, len, "\010OOK")) {
2224+
GvMULTI_on(gv);
2225+
HV *hv = GvHVn(gv);
2226+
hv_magic(hv, NULL, PERL_MAGIC_hook);
2227+
}
2228+
break;
22222229
case '\014':
22232230
if ( memEQs(name, len, "\014AST_FH") || /* ${^LAST_FH} */
22242231
memEQs(name, len, "\014AST_SUCCESSFUL_PATTERN")) /* ${^LAST_SUCCESSFUL_PATTERN} */

intrpvar.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ thread's copy.
130130
=cut
131131
*/
132132

133-
PERLVAR(I, localizing, U8) /* are we processing a local() list? */
133+
PERLVAR(I, localizing, U8) /* are we processing a local() list?
134+
0 = no, 1 = localizing, 2 = delocalizing */
134135
PERLVAR(I, in_eval, U8) /* trap "fatal" errors? */
135136
PERLVAR(I, defgv, GV *) /* the *_ glob */
136137

@@ -495,6 +496,9 @@ PERLVAR(I, origfilename, char *)
495496
PERLVARI(I, xsubfilename, const char *, NULL)
496497
PERLVAR(I, diehook, SV *)
497498
PERLVAR(I, warnhook, SV *)
499+
/* keyword hooks*/
500+
PERLVARI(I, hook__require__before, SV *,NULL)
501+
PERLVARI(I, hook__require__after, SV *,NULL)
498502

499503
/* switches */
500504
PERLVAR(I, patchlevel, SV *)

mg.c

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1748,7 +1748,8 @@ Perl_magic_setsig(pTHX_ SV *sv, MAGIC *mg)
17481748
For magic_clearsig, we don't change the warnings handler if it's
17491749
set to the &PL_warnhook. */
17501750
svp = &PL_warnhook;
1751-
} else if (sv) {
1751+
}
1752+
else if (sv) {
17521753
SV *tmp = sv_newmortal();
17531754
Perl_croak(aTHX_ "No such hook: %s",
17541755
pv_pretty(tmp, s, len, 0, NULL, NULL, 0));
@@ -1820,8 +1821,9 @@ Perl_magic_setsig(pTHX_ SV *sv, MAGIC *mg)
18201821
if (i) {
18211822
(void)rsignal(i, PL_csighandlerp);
18221823
}
1823-
else
1824+
else {
18241825
*svp = SvREFCNT_inc_simple_NN(sv);
1826+
}
18251827
} else {
18261828
if (sv && SvOK(sv)) {
18271829
s = SvPV_force(sv, len);
@@ -1891,6 +1893,92 @@ Perl_magic_setsigall(pTHX_ SV* sv, MAGIC* mg)
18911893
return 0;
18921894
}
18931895

1896+
int
1897+
Perl_magic_clearhook(pTHX_ SV *sv, MAGIC *mg)
1898+
{
1899+
PERL_ARGS_ASSERT_MAGIC_CLEARHOOK;
1900+
1901+
magic_sethook(NULL, mg);
1902+
return sv_unmagic(sv, mg->mg_type);
1903+
}
1904+
1905+
/* sv of NULL signifies that we're acting as magic_clearhook. */
1906+
int
1907+
Perl_magic_sethook(pTHX_ SV *sv, MAGIC *mg)
1908+
{
1909+
SV** svp = NULL;
1910+
STRLEN len;
1911+
const char *s = MgPV_const(mg,len);
1912+
1913+
PERL_ARGS_ASSERT_MAGIC_SETHOOK;
1914+
1915+
if (memEQs(s, len, "require__before")) {
1916+
svp = &PL_hook__require__before;
1917+
}
1918+
else if (memEQs(s, len, "require__after")) {
1919+
svp = &PL_hook__require__after;
1920+
}
1921+
else {
1922+
SV *tmp = sv_newmortal();
1923+
Perl_croak(aTHX_ "Attempt to set unknown hook '%s' in %%{^HOOK}",
1924+
pv_pretty(tmp, s, len, 0, NULL, NULL, 0));
1925+
}
1926+
if (sv && SvOK(sv) && (!SvROK(sv) || SvTYPE(SvRV(sv))!= SVt_PVCV))
1927+
croak("${^HOOK}{%.*s} may only be a CODE reference or undef", (int)len, s);
1928+
1929+
if (svp) {
1930+
if (*svp)
1931+
SvREFCNT_dec(*svp);
1932+
1933+
if (sv)
1934+
*svp = SvREFCNT_inc_simple_NN(sv);
1935+
else
1936+
*svp = NULL;
1937+
}
1938+
1939+
return 0;
1940+
}
1941+
1942+
int
1943+
Perl_magic_sethookall(pTHX_ SV* sv, MAGIC* mg)
1944+
{
1945+
PERL_ARGS_ASSERT_MAGIC_SETHOOKALL;
1946+
PERL_UNUSED_ARG(mg);
1947+
1948+
if (PL_localizing == 1) {
1949+
SAVEGENERICSV(PL_hook__require__before);
1950+
PL_hook__require__before = NULL;
1951+
SAVEGENERICSV(PL_hook__require__after);
1952+
PL_hook__require__after = NULL;
1953+
}
1954+
else
1955+
if (PL_localizing == 2) {
1956+
HV* hv = (HV*)sv;
1957+
HE* current;
1958+
hv_iterinit(hv);
1959+
while ((current = hv_iternext(hv))) {
1960+
SV* hookelem = hv_iterval(hv, current);
1961+
mg_set(hookelem);
1962+
}
1963+
}
1964+
return 0;
1965+
}
1966+
1967+
int
1968+
Perl_magic_clearhookall(pTHX_ SV* sv, MAGIC* mg)
1969+
{
1970+
PERL_ARGS_ASSERT_MAGIC_CLEARHOOKALL;
1971+
PERL_UNUSED_ARG(mg);
1972+
PERL_UNUSED_ARG(sv);
1973+
1974+
SvREFCNT_dec_set_NULL(PL_hook__require__before);
1975+
1976+
SvREFCNT_dec_set_NULL(PL_hook__require__after);
1977+
1978+
return 0;
1979+
}
1980+
1981+
18941982
int
18951983
Perl_magic_setisa(pTHX_ SV *sv, MAGIC *mg)
18961984
{

mg_names.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
{ PERL_MAGIC_substr, "substr(x)" },
4949
{ PERL_MAGIC_nonelem, "nonelem(Y)" },
5050
{ PERL_MAGIC_defelem, "defelem(y)" },
51+
{ PERL_MAGIC_hook, "hook(Z)" },
52+
{ PERL_MAGIC_hookelem, "hookelem(z)" },
5153
{ PERL_MAGIC_lvref, "lvref(\\)" },
5254
{ PERL_MAGIC_checkcall, "checkcall(])" },
5355
{ PERL_MAGIC_extvalue, "extvalue(^)" },

mg_raw.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@
8484
"/* nonelem 'Y' Array element that does not exist */" },
8585
{ 'y', "want_vtbl_defelem | PERL_MAGIC_VALUE_MAGIC",
8686
"/* defelem 'y' Shadow \"foreach\" iterator variable / smart parameter vivification */" },
87+
{ 'Z', "want_vtbl_hook",
88+
"/* hook 'Z' %{^HOOK} hash */" },
89+
{ 'z', "want_vtbl_hookelem",
90+
"/* hookelem 'z' %{^HOOK} hash element */" },
8791
{ '\\', "want_vtbl_lvref",
8892
"/* lvref '\\' Lvalue reference constructor */" },
8993
{ ']', "want_vtbl_checkcall | PERL_MAGIC_VALUE_MAGIC",

mg_vtable.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
#define PERL_MAGIC_nonelem 'Y' /* Array element that does not exist */
5757
#define PERL_MAGIC_defelem 'y' /* Shadow "foreach" iterator variable /
5858
smart parameter vivification */
59+
#define PERL_MAGIC_hook 'Z' /* %{^HOOK} hash */
60+
#define PERL_MAGIC_hookelem 'z' /* %{^HOOK} hash element */
5961
#define PERL_MAGIC_lvref '\\' /* Lvalue reference constructor */
6062
#define PERL_MAGIC_checkcall ']' /* Inlining/mutation of call to this CV */
6163
#define PERL_MAGIC_extvalue '^' /* Value magic available for use by extensions */
@@ -75,6 +77,8 @@ enum { /* pass one of these to get_vtbl */
7577
want_vtbl_envelem,
7678
want_vtbl_hints,
7779
want_vtbl_hintselem,
80+
want_vtbl_hook,
81+
want_vtbl_hookelem,
7882
want_vtbl_isa,
7983
want_vtbl_isaelem,
8084
want_vtbl_lvref,
@@ -114,6 +118,8 @@ EXTCONST char * const PL_magic_vtable_names[magic_vtable_max] = {
114118
"envelem",
115119
"hints",
116120
"hintselem",
121+
"hook",
122+
"hookelem",
117123
"isa",
118124
"isaelem",
119125
"lvref",
@@ -176,6 +182,8 @@ EXT_MGVTBL PL_magic_vtables[magic_vtable_max] = {
176182
{ 0, Perl_magic_setenv, 0, Perl_magic_clearenv, 0, 0, 0, 0 },
177183
{ 0, 0, 0, Perl_magic_clearhints, 0, 0, 0, 0 },
178184
{ 0, Perl_magic_sethint, 0, Perl_magic_clearhint, 0, 0, 0, 0 },
185+
{ 0, Perl_magic_sethookall, 0, Perl_magic_clearhookall, 0, 0, 0, 0 },
186+
{ 0, Perl_magic_sethook, 0, Perl_magic_clearhook, 0, 0, 0, 0 },
179187
{ 0, Perl_magic_setisa, 0, Perl_magic_clearisa, 0, 0, 0, 0 },
180188
{ 0, Perl_magic_setisa, 0, 0, 0, 0, 0, 0 },
181189
{ 0, Perl_magic_setlvref, 0, 0, 0, 0, 0, 0 },
@@ -224,6 +232,8 @@ EXT_MGVTBL PL_magic_vtables[magic_vtable_max];
224232
#define PL_vtbl_fm PL_magic_vtables[want_vtbl_fm]
225233
#define PL_vtbl_hints PL_magic_vtables[want_vtbl_hints]
226234
#define PL_vtbl_hintselem PL_magic_vtables[want_vtbl_hintselem]
235+
#define PL_vtbl_hook PL_magic_vtables[want_vtbl_hook]
236+
#define PL_vtbl_hookelem PL_magic_vtables[want_vtbl_hookelem]
227237
#define PL_vtbl_isa PL_magic_vtables[want_vtbl_isa]
228238
#define PL_vtbl_isaelem PL_magic_vtables[want_vtbl_isaelem]
229239
#define PL_vtbl_lvref PL_magic_vtables[want_vtbl_lvref]

perl.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,10 @@ perl_destruct(pTHXx)
932932
PL_warnhook = NULL;
933933
SvREFCNT_dec(PL_diehook);
934934
PL_diehook = NULL;
935+
SvREFCNT_dec(PL_hook__require__before);
936+
PL_hook__require__before = NULL;
937+
SvREFCNT_dec(PL_hook__require__after);
938+
PL_hook__require__after = NULL;
935939

936940
/* call exit list functions */
937941
while (PL_exitlistlen-- > 0)

pod/perldiag.pod

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2263,6 +2263,18 @@ the C<encoding> pragma, is no longer supported as of Perl 5.26.0.
22632263
Setting it to anything other than C<undef> is a fatal error as of Perl
22642264
5.28.
22652265

2266+
=item ${^HOOK}{%s} may only be a CODE reference or undef
2267+
2268+
(F) You attempted to assign something other than undef or a CODE ref to
2269+
C<%{^HOOK}>. Hooks may only be CODE refs. See L<perlvar/%{^HOOK}> for
2270+
details.
2271+
2272+
=item Attempt to set unknown hook '%s' in %{^HOOK}
2273+
2274+
(F) You attempted to assign something other than undef or a CODE ref to
2275+
C<%{^HOOK}>. Hooks may only be CODE refs. See L<perlvar/%{^HOOK}> for
2276+
details.
2277+
22662278
=item entering effective %s failed
22672279

22682280
(F) While under the C<use filetest> pragma, switching the real and
@@ -3961,11 +3973,17 @@ can vary from one line to the next.
39613973

39623974
=item Missing or undefined argument to %s
39633975

3964-
(F) You tried to call require or do with no argument or with an undefined
3965-
value as an argument. Require expects either a package name or a
3966-
file-specification as an argument; do expects a filename. See
3976+
(F) You tried to call C<require> or C<do> with no argument or with an
3977+
undefined value as an argument. Require expects either a package name or
3978+
a file-specification as an argument; do expects a filename. See
39673979
L<perlfunc/require EXPR> and L<perlfunc/do EXPR>.
39683980

3981+
=item Missing or undefined argument to %s via %{^HOOK}{require__before}
3982+
3983+
(F) A C<%{^HOOK}{require__before}> hook rewrote the name of the file being
3984+
compiled with C<require> or C<do> with an empty string an undefined value
3985+
which is forbidden. See L<perlvar/%{^HOOK}> and L<perlfunc/require EXPR>.
3986+
39693987
=item Missing right brace on \%c{} in regex; marked by S<<-- HERE> in m/%s/
39703988

39713989
(F) Missing right brace in C<\x{...}>, C<\p{...}>, C<\P{...}>, or C<\N{...}>.

0 commit comments

Comments
 (0)