| Commit message (Collapse) | Author | Age | Files | Lines |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This reverts commit 137da2b05b4b7628115049f343163bdaf2c30dbb. See the
"How about having a recommended way to add constant subs dynamically?"
thread on perl5-porters, specifically while it sucks that we have this
bug, it's been documented to work this way since 5.003 in "Constant
Functions" in perlsub:
If the result after optimization and constant folding is either a
constant or a lexically-scoped scalar which has no other references,
then it will be used in place of function calls made without C<&>
-- http://perldoc.perl.org/perlsub.html#Constant-Functions
Since we've had this documented bug for a long time we should introduce
this fix in a deprecation cycle rather than silently slowing down code
that assumes it's going to be optimized by constant folding.
I didn't revert the tests it t/op/sub.t, but turned them into TODO tests
instead.
Conflicts:
t/op/sub.t
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Currently all OP_NULL/OP_SCOPE/OP_SCALAR/OP_LINESEQ ops (which all map at
run time to pp_null()) are eliminated from op_next chains *except* ones at
the head of a chain (e.g. pointed to by o->op_other).
The API of peep()/rpeep() makes it difficult to directly do this within
the function itself, as it has no return value - and thus
RPEEP(o->op_other) has no way to update op_other to skip the first op if
it happens to be a null or whatever.
Instead, we add a small helper function, S_prune_chain_head(), and
always call it after we call peep, e.g.
CALL_PEEP(PL_main_start);
finalize_optree(PL_main_root);
+S_prune_chain_head(aTHX_ &PL_main_start);
rpeep() is also complicated by its recursion reduction mechanism, where
it saves the addresses of several ops before recursing on them. I had to
change this so that it saves the addresses of the addresses of the ops
instead, so they can be updated: i.e. rather than saving o->op_other,
it saves &(o->op_other).
With this commit, nothing in the test suite triggers executing pp_null(),
execpt OP_REGCMAYBE and S_fold_constants(). I verified this with the
following hacky diff:
>>>>diff --git a/op.c b/op.c
>>>>index 716c684..819a717 100644
>>>>--- a/op.c
>>>>+++ b/op.c
>>>>@@ -3489,6 +3489,7 @@ S_op_integerize(pTHX_ OP *o)
>>>> return o;
>>>> }
>>>>
>>>>+int XXX_folding = 0;
>>>> static OP *
>>>> S_fold_constants(pTHX_ OP *o)
>>>> {
>>>>@@ -3504,6 +3505,7 @@ S_fold_constants(pTHX_ OP *o)
>>>> SV * const olddiehook = PL_diehook;
>>>> COP not_compiling;
>>>> dJMPENV;
>>>>+ int XXX_folding_old = XXX_folding;
>>>>
>>>> PERL_ARGS_ASSERT_FOLD_CONSTANTS;
>>>>
>>>>@@ -3583,11 +3585,13 @@ S_fold_constants(pTHX_ OP *o)
>>>> assert(IN_PERL_RUNTIME);
>>>> PL_warnhook = PERL_WARNHOOK_FATAL;
>>>> PL_diehook = NULL;
>>>>+ XXX_folding = 1;
>>>> JMPENV_PUSH(ret);
>>>>
>>>> switch (ret) {
>>>> case 0:
>>>> CALLRUNOPS(aTHX);
>>>>+ XXX_folding = XXX_folding_old;
>>>> sv = *(PL_stack_sp--);
>>>> if (o->op_targ && sv == PAD_SV(o->op_targ)) { /* grab pad temp? */
>>>> #ifdef PERL_MAD
>>>>@@ -3608,10 +3612,12 @@ S_fold_constants(pTHX_ OP *o)
>>>> case 3:
>>>> /* Something tried to die. Abandon constant folding. */
>>>> /* Pretend the error never happened. */
>>>>+ XXX_folding = XXX_folding_old;
>>>> CLEAR_ERRSV();
>>>> o->op_next = old_next;
>>>> break;
>>>> default:
>>>>+ XXX_folding = XXX_folding_old;
>>>> JMPENV_POP;
>>>> /* Don't expect 1 (setjmp failed) or 2 (something called my_exit) */
>>>> PL_warnhook = oldwarnhook;
>>>>diff --git a/pp_hot.c b/pp_hot.c
>>>>index 36eac2b..ccb582f 100644
>>>>--- a/pp_hot.c
>>>>+++ b/pp_hot.c
>>>>@@ -68,9 +68,16 @@ PP(pp_gvsv)
>>>> RETURN;
>>>> }
>>>>
>>>>+extern int XXX_folding;
>>>> PP(pp_null)
>>>> {
>>>> dVAR;
>>>>+ if (!XXX_folding && PL_op->op_type != OP_REGCMAYBE) {
>>>>+ sv_dump((SV*)find_runcv(0));
>>>>+ op_dump(PL_op);
>>>>+ op_dump((OP*)PL_curcop);
>>>>+ assert(0);
>>>>+ }
>>>> return NORMAL;
>>>> }
>>>>
|
|
|
|
|
|
|
|
|
|
|
|
| |
Perl_rpeep() currently spots "empty" ops like OP_NULL, OP_SCOPE
and elides them from the op_next chain (by setting oldop->op_next to point
to the op following the current op). However, if rpeep() itself
op_null()'s the current op, then when the main loop is re-entered, that
mechanism is bypassed. Modify re-entry to the loop in this case so that
the just nulled op re-processed and thus elided.
(Also document what the OP_SASSIGN/OP_SUBSTR optimisation is doing;
studying that was what originally led me to this general fix.)
|
|
|
|
|
|
|
|
| |
Perl_rpeep() elides OP_NULLs etc in the middle of an op_next chain, but
not at the start or end. Doing it at the start is hard (and not addressed
here); doing it at the end is trivial, and it just looks like a mistake in
the original code (there since 1994) that was (incorrectly) worried about
following through a null pointer.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
In something like this
eval { 1 while 1 };
$x = $a[0];
The optimising of the while loop makes Perl_rpeep() miss processing the
chain of ops from the OP_LEAVETRY onwards. So in the code above for
example, the alem wont be optimised into an alemfast.
Fix this by explicitly recursing into the entertry->op_other branch
(which actually points at the leavetry).
The infinite loop above can be broken by, for example, a signal handler
calling die.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
When a sort with a code block, like sort { BLOCK } arg, ...
is compiled, it comes out like
sort
pushmark
null
scope
BLOCK
arg
...
(The 'scope' may be instead be 'ex-leave' depending on circumstances).
At run time, pp_sort() navigates its way down from the sort op to find the
start op of the BLOCK. We can shorten this process slightly by storing the
start of BLOCK in the otherwise unused op_next field of the OP_NULL.
Effectively we are using the null->op_next field as a surrogate op_other
field for the op_sort (which doesn't have a spare field we could store
the pointer in).
The main point of this commit however is not the slight speed up from
skipping a couple of pointer follows at run-time; rather that it will
shortly allow us to trim any null ops from the beginning of the BLOCK. We
can't do this directly, as that would involve changing the scope->op_first
pointer, which might confuse B:: type modules.
|
|
|
|
|
|
|
|
|
|
|
| |
In rpeep(), we check whether the OP_SORT has a code block as the first arg
(e.g. sort {BLOCK} ...) by testing for OPf_STACKED, then looking for
an OP_SCOPE or ex-OP_LEAVE. However, ck_sort() has already checked for
this situation and set the OPf_SPECIAL flag. So just just check for this
flag in rpeep(); leave in the OP_SCOPE/ex-OP_LEAVE checks as just
assertions.
Also, add some commentary to ck_sort() and S_simplify_sort().
|
|
|
|
|
|
|
|
|
|
| |
Use aelemfast for literal index array access where the index is in the
range -128..127, rather than 0..255.
You'd expect something like $a[-1] or $a[-2] to be a lot more common than
$a[100] say. In fact a quick CPAN grep shows 66 distributions
matching /\$\w+\[\d{3,}\]/, but "at least" 1000 matching /\$\w+\[\-\d\]/.
And most of the former appear to be table initialisations.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Under threaded builds, GVs for OPs are stored in the pad rather than
being directly attached to the op. For some reason, all such GV's were
getting the SvPADTMP flag set. There seems to be be no good reason for
this, and after skipping setting the flag, all tests still pass.
The advantage of not setting this flag is that there are quite a few
hot places in the code that do
if (SvPADTMP(sv) && !IS_PADGV(sv)) {
...
I've replaced them all with
if (SvPADTMP(sv)) {
assert(!IS_PADGV(sv));
...
Since the IS_PADGV() macro expands to something quite heavyweight, this is
quite a saving: for example this commit reduces the size of pp_entersub by
111 bytes.
|
|
|
|
| |
kid is always dereferenced before the assert.
|
|
|
|
|
|
| |
Apologies. I'd applied Reini's patch to my optimization branch. I think
it got lost when I switched to Dave's variant my original code (which
didn't have Reini's C90 fix). Sorry!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This is an optimization for OP trees that involve list OPs in list
context. In list context, the list OP's first child, a pushmark, will do
what its name claims and push a mark to the mark stack, indicating the
start of a list of parameters to another OP. Then the list's other
child OPs will do their stack pushing. Finally, the list OP will be
executed and do nothing but undo what the pushmark has done. This is
because the main effect of the list OP only really kicks in if it's
not in array context (actually, it should probably only kick in if
it's in scalar context, but I don't know of any valid examples of
list OPs in void contexts).
This optimization is quite a measurable speed-up for array or hash
slicing and some other situations. Another (contrived) example is
that (1,2,(3,4)) now actually is the same, performance-wise as
(1,2,3,4), albeit that's rarely relevant.
The price to pay for this is a slightly convoluted (by standards other
than the perl core) bit of optimization logic that has to do minor
look-ahead on certain OPs in the peephole optimizer.
A number of tests failed after the first attack on this problem. The
failures were in two categories:
a) Tests that are sensitive to details of the OP tree structure and did
verbatim text comparisons of B::Concise output (ouch). These are just
patched according to the new red in this commit.
b) Test that validly failed because certain conditions in op.c were
expecting OP_LISTs where there are now OP_NULLs (with op_targ=OP_LIST).
For these, the respective conditions in op.c were adjusted.
The change includes modifying B::Deparse to handle the new OP tree
structure in the face of nulled OP_LISTs.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
For example,
if (OP_TYPE_IS_OR_WAS(o, OP_LIST))
...
is now available instead of either of the following:
if ( o
&& ( o->op_type == OP_LIST
|| (o->op_type == OP_NULL
&& o->op_targ == OP_LIST) ) )
...
if ( o &&
(o->op_type == OP_NULL ? o->op_targ ? o->op_type) == OP_LIST )
...
In case the above logic is a bit unclear: It checks whether that OP is
an OP_LIST or used to be one before being NULLed using op_null.
(FTR, the resulting OP_NULLs have their op_targ set to the old OP type).
This sort of check (and it's reverse "isn't and didn't use to be") are a
relatively common pattern in the part of op.c that tries to intuit
structures from optimization-mangled OP trees. Hopefully, using these
macros will make some code a fair amount clearer.
|
| |
|
|
|
|
|
|
| |
av_tindex is a more clearly named synonym for av_len, available starting
in v5.18. This changes the core uses to it, including modules in /ext,
which are not dual-lifed.
|
| |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Commit 2e73d70e52 broke this (made it vivify) by propagating lvalue
context to the branches of || and && (to fix another bug). It broke
App::JobLog as a result.
Because foreach does not do defelem magic (i.e., it vivifies), this
ends up extending vivification to happen where it did not before.
Fixing foreach to do defelem magic (create ‘deferred element’ scalars,
the way sub calls do, to avoid vivifying immediately) would be another
way to fix this, but it is controversial. See ticket #2166.
So, if either argument to || (or &&) is a vivifying op, don’t propa-
gate the lvalue context, unless this is the return value of an lvalue
sub (necessary for if/else with implicit return to work correctly in
lvalue subs).
|
| |
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Brings:
sub { my $x; my $y; return 1; }
Up to speed with:
sub { my ($x, $y); return 1; }
|
| |
|
| |
|
|
|
|
|
|
| |
from https://rt.perl.org/Public/Bug/Display.html?id=121077#txn-1277679
Tony Cook: s/Neutralized/Neutralizes/
|
| |
|
|
|
|
| |
This causes op.o to drop by 56 bytes for me under clang.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Make perls compiled with -Accflags=-DPERL_DEBUG_READONLY_COW to turn
COW buffer violations into crashes.
We do this using mmap to allocate memory and then mprotect to mark
memory as read-only when buffers are shared.
We have to do this at the safesysmalloc level, because some code does
SvPV_set with buffers it allocates on its own via safemalloc().
Unfortunately this means many things are allocated using mmap that
will never be marked read-only, slowing things down considerably, but
I see no other way.
Because munmap and mprotect need to know the length, we use the
existing sTHX/perl_memory_debug_header mechanism used already by
PERL_TRACK_MEMPOOL and store the size there (as PERL_POISON already
does when PERL_TRACK_MEMPOOL is enabled). perl_memory_debug_header is
a struct positioned at the beginning of every allocated buffer, for
tracking things.
|
|
|
|
| |
Spotted by Matthew Horsfall
|
| |
|
|
|
|
|
| |
We need a better name for the experimental category, but I have not
thought of one, even after sleeping on it.
|
|
|
|
|
|
|
|
|
| |
Under clang, this reduces the size of op.o by 16 bytes.
$ clang -v
Apple LLVM version 4.2 (clang-425.0.27) (based on LLVM 3.2svn)
Target: x86_64-apple-darwin12.5.0
Thread model: posix
|
|
|
|
| |
plus some typo fixes. I probably changed some things in perlintern, too.
|
|
|
|
|
|
| |
This reverts commit c1cec775e9019cc8ae244d4db239a7ea5c0b343e.
See ticket #120864.
|
| |
|
|
|
|
|
| |
flags param was poorly designed and didn't have a formal api. Replace it
with the bool it really is. See #115736 for details.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The cop address for each breakable line was being stored in the IVX
slot of ${"_<$file"}[$line]. This value itself, writable from Perl
space, was being used as the address of the op to be flagged, whenever
a breakpoint was set.
This meant writing to ${"_<$file"}[$line] and assigning a number (like
42) would cause perl to use 42 as an op address, and crash when trying
to flag the op.
Furthermore, since the array holding the lines could outlive the ops,
setting a breakpoint on the op could write to freed memory or to an
unrelated op (even a different type), potentially changing the beha-
viour of unrelated code.
This commit solves those pitfalls by moving breakpoints into a global
breakpoint bitfield. Dbstate ops now have an extra field on the end
holding a sequence number, representing which bit holds the breakpoint
for that op.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
According to the original bug report, ‘do{print("foobar");}until(1)}’
crashes. In recent perls it doesn’t, partly because of the slab allo-
cator, partly because such crashes are naturally fleeting.
‘foo while bar’ and ‘foo until bar’ make their way through newLOOPOP,
which then usually calls S_new_logop, to create and AND or OR op with
special pointers that turn it into a loop.
Because S_new_logop knows about folding ‘$x if 1’ down to a simple $x,
and because ‘do{foo()} while 0’ should still execute the do block,
newLOOPOP skips the call to S_new_logop in that case.
Hence, it assumes that if it has seen a do block on its lhs, then
S_new_logop must return an AND or OR op.
‘foo until bar’ is actually changed early on (in perly.y) to ‘foo
while !bar’, before it reaches newLOOPOP. Constant folding usually
folds !1 down to a simple ""/0 (actually &PL_sv_no), so newLOOPOP sees
‘foo while 0’ for ‘foo until 1’.
If constant folding fails (e.g., because the parser has seen an
unmatched } and constant folding is skipped after such errors), then
newLOOPOP will see the unfolded !1.
S_new_logop has a special optimisation that changes ‘!foo && bar’ to
‘foo || bar’, etc.
That optimisation allows it to ‘see through’ the unoptimised !1 (a NOT
with a CONST kid) and get to the constant, folding the resulting op
and returning something that newLOOPOP is not expecting to be folded.
In the case of ‘do{print("foobar");}until(1)}’, it optimises the do
block away, which is wrong.
So newLOOPOP reads past the end of the op in this line:
o->op_next = ((LOGOP*)cUNOPo->op_first)->op_other;
because it is treating an SVOP as a LOGOP.
I can trigger this condition by defeating constant folding some other
way. Croaking in boolean overloading, but just the first time, will
do that, and crashes with blead:
{ package o; use overload bool => sub { die unless $::ok++; return 1 } }
use constant OK => bless [], o::;
do{print("foobar");}until OK;
__END__
Bus error: 10
My reading of the source code leads me to believe that this bad read
has been present since perl 5.000. But back then it was not possible
to trigger it with this particular test case involving fatal overload-
ing (as of b7f7fd0bb it seems*), but ‘do{print("foobar");}until(1)}’
would have triggered it.
* Thanks to Matthew Horsfall for finding it.
|
| |
|
|
|
|
|
| |
This message is very unhelpful. This was brought up in
ticket #115688.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This makes:
sub baz { return $cat; }
Behave like:
sub baz { $cat; }
Which is notably faster.
Unpatched:
./perl -Ilib/ ~/stuff/bench.pl
Benchmark: timing 40000000 iterations of normal, ret...
normal: 3 wallclock secs ( 1.60 usr + 0.01 sys = 1.61 CPU) @ 24844720.50/s (n=40000000)
ret: 3 wallclock secs ( 2.08 usr + 0.00 sys = 2.08 CPU) @ 19230769.23/s (n=40000000)
Patched:
./perl -Ilib ~/stuff/bench.pl
Benchmark: timing 40000000 iterations of aret, normal...
normal: 2 wallclock secs ( 1.72 usr + 0.00 sys = 1.72 CPU) @ 23255813.95/s (n=40000000)
ret: 2 wallclock secs ( 1.72 usr + 0.00 sys = 1.72 CPU) @ 23255813.95/s (n=40000000)
The difference in OP trees can be seen here:
Unpatched:
$ perl -MO=Concise,baz -e 'sub baz { return $cat }'
main::baz:
5 <1> leavesub[1 ref] K/REFC,1 ->(end)
- <@> lineseq KP ->5
1 <;> nextstate(main 1 -e:1) v ->2
4 <@> return K ->5
2 <0> pushmark s ->3
- <1> ex-rv2sv sK/1 ->4
3 <#> gvsv[*cat] s ->4
-e syntax OK
Patched:
$ ./perl -Ilib -MO=Concise,baz -e 'sub baz { return $cat }'
main::baz:
3 <1> leavesub[1 ref] K/REFC,1 ->(end)
- <@> lineseq KP ->3
1 <;> nextstate(main 1 -e:1) v ->2
- <@> return K ->-
- <0> pushmark s ->2
- <1> ex-rv2sv sK/1 ->-
2 <$> gvsv(*cat) s ->3
-e syntax OK
(Includes some modifications from Steffen)
|
|
|
|
|
|
|
| |
They are marked PADTMP, which causes them to be copied in any contexts
where readonliness makes a difference, so marking them as read-only
does not change the behaviour. What it does is allow a future commit
to implement string swiping for PADTMPs.
|
|
|
|
|
|
| |
As of commit 53a7735b62aee146 (May 2007) Perl_vload_module() wraps its call
to Perl_utilize() with ENTER/LEAVE, so there's no longer a need for callers
of Perl_load_module() to also wrap with ENTER/LEAVE.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This avoids the stack moving underneath anything that directly or indirectly
calls Perl_load_module().
[Committer’s note:
This fixes bug #119993.
Furthermore, under STRESS_REALLOC, t/io/layers.t was crashing like this:
$ ./perl -Ilib -e ' open(UTF, "<:raw:encoding(utf8)", 'tmp75851B') or die $!; ref #blahblahblahblahblahblahblahblahblah'
Segmentation fault: 11
(The comment seems to be necessary to make it crash.)
It was happening because open() was causing a module to be loaded
while the arguments to open() were still on the stack.
]
|
| |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
An OP_AND/OP_OR in void context provides a short circuit
through ->op_other that can be used if AND/OR ops contained
within it jump out early. Use that short circuit.
Previously:
$ ./perl -Ilib -MO=Concise -e 'if ($aa || $bb) {}'
8 <@> leave[1 ref] vKP/REFC ->(end)
1 <0> enter ->2
2 <;> nextstate(main 3 -e:1) v:{ ->3
- <1> null vK/1 ->8
6 <|> and(other->7) vK/1 ->8
- <1> null sK/1 ->6
4 <|> or(other->5) sK/1 ->6 <-- Not optimised
- <1> ex-rv2sv sK/1 ->4
3 <$> gvsv(*aa) s ->4
- <1> ex-rv2sv sK/1 ->-
5 <$> gvsv(*bb) s ->6
- <@> scope vK ->-
7 <0> stub v ->8
Now:
$ ./perl -Ilib -MO=Concise -e 'if ($aa || $bb) {}'
8 <@> leave[1 ref] vKP/REFC ->(end)
1 <0> enter ->2
2 <;> nextstate(main 3 -e:1) v:{ ->3
- <1> null vK/1 ->8
6 <|> and(other->7) vK/1 ->8
- <1> null sK/1 ->6
4 <|> or(other->5) sK/1 ->7 <-- Short circuited
- <1> ex-rv2sv sK/1 ->4
3 <$> gvsv(*aa) s ->4
- <1> ex-rv2sv sK/1 ->-
5 <$> gvsv(*bb) s ->6
- <@> scope vK ->-
7 <0> stub v ->8
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
See the thread starting at
<20131115042923.7514.qmail@lists-nntp.develooper.com>.
Commits 7004ee4937 and a61818571 changed subroutine redefinition to
null the GvCV slot before freeing the CV, so that destructors won’t
see GvCV without a reference count. That turns a double free into a
memory leak.
Kent Fredric explains it nice and clearly:
> sub foo{} # A
> bless \&foo;
> DESTROY { *foo = sub {} # C }
> eval "sub foo{} "; # B
>
> Previous behaviour was:
>
> B replaces A, triggers DESTROY, which triggers C replacing A, and this
> invoked a double free, because C , triggering the removal of A,
> happened while A still existed ( ?? )
>
> So the change fixes this, so that A is removed from the symbol table
> before DESTROY triggers , so that C is creating a "new" symbol,
> effectively, and the problem is that C is then clobbered by the B
> replacing the slot, after the DESTROY.
So C leaks.
This commit fixes it by changing the SvREFCNT_dec into SAVEFREESV,
essentially delaying the DESTROY until after the subroutine redefini-
tion is complete.
This does mean that C is what ends up in the glob afterwards; but as
long as perl’s own bookkeeping is thrown off, we can leave it to the
user (the Perl programmer) to handle the consequences of naughty
destructors.
|