Subject: | CVREF does not specify a stack frame |
If a function is called recursively, the same CVREF will
be assigned to each call, even though those calls have
different stack frames. Therefore, the functions
ref_to_lexical, lexalias, and lexical_alias cannot
be correctly implemented with the given prototypes.
My quick guess (without thoroughly understanding the code) is
that the '0' hardcoded in Util.c:204:
ref_to_var = av_fetch(padv, i, 0);
always references the topmost scratchpad.
This level should be variable instead.
For single-threaded execution, probably you could count the number
of times the same CVREF is obtained before level $i the caller stack,
and return it as $j in
($cvref, $j) = frame_to_cvref($i);
so that the '0' above can be replaced with $j provided in a call:
$ref = ref_to_lexical('$lexical', $cvref, $j);
(order of arguments changed as a suggestion, for composability
and so that $j can default to zero).
In a multi-threaded environment, you're in deeper trouble.
See PadWalker for a solution, I hope.
Tests (working with PadWalker, failing with Lexical::Util) attached.
Thanks for factoring this functionality out, by the way;
I hope you can fix it so it's usable!
-------
This is perl, v5.8.6 built for darwin-thread-multi-2level
OS X 10.4.8
Subject: | padwalker-recursion.t |
use Test::More tests => 7;
use PadWalker qw/peek_my/;
foo(0);
sub foo
{
my $depth = $_[0];
my $y = $depth;
if($depth > 0) {
my $href = peek_my(1);
my $yref = $$href{'$y'};
$$yref = -1 * $$yref;
}
is($y, $depth, "Depth $depth: Before foo(), I have not been negated");
if($depth < 3) {
foo($depth + 1);
is($y, -1 * $depth, "Depth $depth: after foo(), I have been negated");
}
}
Subject: | recursion-bug.t |
use Test::More tests => 7;
use Lexical::Util qw/frame_to_cvref ref_to_lexical/;
foo(0);
sub foo
{
my $depth = $_[0];
my $y = $depth;
if($depth > 0) {
my $yref = ref_to_lexical(frame_to_cvref(1), '$y');
$$yref = -1 * $$yref;
}
is($y, $depth, "Depth $depth: Before foo(), I have not been negated");
if($depth < 3) {
foo($depth + 1);
is($y, -1 * $depth, "Depth $depth: after foo(), I have been negated");
}
}