diff options
author | Father Chrysostomos <sprout@cpan.org> | 2012-08-03 09:23:15 -0700 |
---|---|---|
committer | Father Chrysostomos <sprout@cpan.org> | 2012-09-15 22:45:05 -0700 |
commit | 6d5c21479838db78689e08afd075ef4e9100ef0d (patch) | |
tree | 44db00c7bbbae895f1e3208ffcd3a43b719373cb /t/cmd | |
parent | fead5351141134064bc3069932e04930bd96eb0e (diff) | |
download | perl-6d5c21479838db78689e08afd075ef4e9100ef0d.tar.gz |
Clone my subs on scope entry
The pad slot for a my sub now holds a stub with a prototype CV
attached to it by proto magic.
The prototype is cloned on scope entry. The stub in the pad is used
when cloning, so any code that references the sub before scope entry
will be able to see that stub become defined, making these behave
similarly:
our $x;
BEGIN { $x = \&foo }
sub foo { }
our $x;
my sub foo { }
BEGIN { $x = \&foo }
Constants are currently not cloned, but that may cause bugs in
pad_push. I’ll have to look into that.
On scope exit, lexical CVs go through leave_scope’s SAVEt_CLEARSV sec-
tion, like lexical variables. If the sub is referenced elsewhere, it
is abandoned, and its proto magic is stolen and attached to a new stub
stored in the pad. If the sub is not referenced elsewhere, it is
undefined via cv_undef.
To clone my subs on scope entry, we create a sequence of introcv and
clonecv ops. See the huge comment in block_end that explains why we
need two separate ops for each CV.
To allow my subs to be defined in inner subs (my sub foo; sub { sub
foo {} }), pad_add_name_pvn and S_pad_findlex now upgrade the entry
for a my sub to a CV to begin with, so that fake entries added to pads
(fake entries are those that reference outer pads) can share the same
CV. Otherwise newMYSUB would have to add the CV to every pad that
closes over the ‘my sub’ declaration. newMYSUB no longer throws away
the initial value replacing it with a new one.
Prototypes are not currently visible to sub calls at compile time,
because the lexer sees the empty stub. A future commit will
solve that.
When I added name heks to CV’s I made mistakes in a few places, by not
turning on the CVf_NAMED flag, or by not clearing the field when free-
ing the hek. Those code paths were not exercised enough by state
subs, so the problems did not show up till now. So this commit fixes
those, too.
One of the tests in lexsub.t, involving foreach loops, was incorrect,
and has been fixed. Another test has been added to the end for a par-
ticular case of state subs closing over my subs that I broke when ini-
tially trying to get sibling my subs to close over each other, before
I had separate introcv and clonecv ops.
Diffstat (limited to 't/cmd')
-rw-r--r-- | t/cmd/lexsub.t | 37 |
1 files changed, 20 insertions, 17 deletions
diff --git a/t/cmd/lexsub.t b/t/cmd/lexsub.t index fa4ccdc5d2..348facf933 100644 --- a/t/cmd/lexsub.t +++ b/t/cmd/lexsub.t @@ -8,7 +8,7 @@ BEGIN { *bar::like = *like; } no warnings 'deprecated'; -plan 106; +plan 107; # -------------------- our -------------------- # @@ -84,9 +84,6 @@ sub bar::c { 43 } # -------------------- state -------------------- # -sub on { $::TODO = ' ' } -sub off { $::TODO = undef } - use 5.01; # state { state sub foo { 44 } @@ -415,33 +412,31 @@ sub mmake_closure { } $sub1 = mmake_closure 48; $sub2 = mmake_closure 49; -on; -is eval { &$sub1 }, 48, 'my sub in closure (1)'; -is eval { &$sub2 }, 49, 'my sub in closure (2)'; +is &$sub1, 48, 'my sub in closure (1)'; +is &$sub2, 49, 'my sub in closure (2)'; # Test that they are cloned in named subs. { use warnings; my $w; local $SIG{__WARN__} = sub { $w .= shift }; eval '#line 65 teetet - sub foom { + sub mfoom { my $x = shift; my sub poom { $x } - eval{\&poom} + \&poom } '; is $w, undef, 'my subs get no "Variable will not stay shared" messages'; - my $poom = foom(27); - my $poom2 = foom(678); - is eval { $poom->() }, 27, 'my subs closing over outer my var (1)'; - is eval { $poom2->() }, 678, 'my subs closing over outer my var (2)'; + my $poom = mfoom(27); + my $poom2 = mfoom(678); + is $poom->(), 27, 'my subs closing over outer my var (1)'; + is $poom2->(), 678, 'my subs closing over outer my var (2)'; my $x = 43; my sub aoeu; for $x (765) { my sub etetetet { $x } - my sub aoeu { $x } + sub aoeu { $x } is etetetet, 765, 'my sub respects for() localisation'; -off; is aoeu, 43, 'unless it is declared outside the for loop'; } } @@ -463,11 +458,9 @@ sub make_anon_with_my_sub{ &$s; # And make sure the my subs were actually cloned. -on; isnt make_anon_with_my_sub->(0), &$s(0), 'my subs in anon subs are cloned'; isnt &$s(0), &$s(0), 'at each invocation of the enclosing sub'; -off; } { my sub BEGIN { exit }; @@ -485,3 +478,13 @@ off; is $w, "Subroutine redef redefined at pygpyf line 56.\n", "sub redefinition warnings from my subs"; } + +# -------------------- Interactions (and misc tests) -------------------- # + +is sub { + my sub s1; + my sub s2 { 3 }; + sub s1 { state sub foo { \&s2 } foo } + s1 + }->()(), 3, 'state sub inside my sub closing over my sub uncle'; + |