Skip Menu |

This queue is for tickets about the PadWalker CPAN distribution.

Report information
The Basics
Id: 47213
Status: open
Priority: 0/
Queue: PadWalker

People
Owner: Nobody in particular
Requestors: nothingmuch [...] woobling.org
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: (no value)



CC: Yuval Kogman <nothingmuch [...] woobling.org>
Subject: [PATCH] Add set_closed_over($cv, $hash)
Date: Sun, 21 Jun 2009 12:37:54 -0400
To: bug-PadWalker [...] rt.cpan.org
From: Yuval Kogman <nothingmuch [...] woobling.org>
--- PadWalker.pm | 8 +++++++- PadWalker.xs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ t/closure.t | 45 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 96 insertions(+), 3 deletions(-) diff --git a/PadWalker.pm b/PadWalker.pm index 7c14f3e..460143d 100644 --- a/PadWalker.pm +++ b/PadWalker.pm @@ -9,7 +9,7 @@ require DynaLoader; require 5.008; @ISA = qw(Exporter DynaLoader); -@EXPORT_OK = qw(peek_my peek_our closed_over peek_sub var_name); +@EXPORT_OK = qw(peek_my peek_our closed_over peek_sub var_name set_closed_over); %EXPORT_TAGS = (all => \@EXPORT_OK); $VERSION = '1.7'; @@ -112,6 +112,12 @@ in other words, the variables which it closes over. This I<does> have reasonable uses: see L<Data::Dump::Streamer>, for example (a future version of which may in fact use C<closed_over>). +=item set_closed_over SUB, HASH_REF + +C<set_closed_over> reassigns the pad variables that are closed over by the subroutine. + +The second argument is a hash of references, much like the one returned from C<closed_over>. + =item var_name LEVEL, VAR_REF =item var_name SUB, VAR_REF diff --git a/PadWalker.xs b/PadWalker.xs index 6e1809c..1840bd3 100644 --- a/PadWalker.xs +++ b/PadWalker.xs @@ -489,6 +489,52 @@ CV* cv; EXTEND(SP, 1); PUSHs(sv_2mortal(newRV_noinc((SV*)ret))); + + +void +set_closed_over(sv, pad) +SV* sv; +HV* pad; + PREINIT: + I32 i; + CV *cv = (CV *)SvRV(sv); + U32 val_depth = CvDEPTH(cv) ? CvDEPTH(cv) : 1; + AV *pad_namelist = (AV*) *av_fetch(CvPADLIST(cv), 0, FALSE); + AV *pad_vallist = (AV*) *av_fetch(CvPADLIST(cv), val_depth, FALSE); + CODE: + for (i=av_len(pad_namelist); i>=0; --i) { + SV** name_ptr = av_fetch(pad_namelist, i, 0); + + if (name_ptr && SvPOKp(*name_ptr)) { + SV* name_sv = *name_ptr; + char* name_str = SvPVX(name_sv); + STRLEN name_len = strlen(name_str); + + if (SvFAKE(name_sv) && 0 == (SvFLAGS(name_sv) & SVpad_OUR)) { + SV **restore_ref = hv_fetch(pad, name_str, name_len, FALSE); + if ( restore_ref ) { + if ( SvROK(*restore_ref) ) { + SV *restore = SvRV(*restore_ref); + SV **orig = av_fetch(pad_vallist, i, 0); + + if ( !orig || !*orig || strcmp(sv_reftype(*orig, 0), sv_reftype(restore, 0)) == 0 ) { + SvREFCNT_inc(restore); + + if ( av_store(pad_vallist, i, restore) == NULL ) + SvREFCNT_dec(restore); + } else { + croak("Incorrect reftype for variable %s (got %s expected %s)", name_str, sv_reftype(restore, 0), sv_reftype(*orig, 0)); + } + } else { + croak("The variable for %s is not a reference", name_str); + } + } + } + } + } + + + void closed_over(cv) CV* cv; diff --git a/t/closure.t b/t/closure.t index d8faf87..354c177 100644 --- a/t/closure.t +++ b/t/closure.t @@ -1,7 +1,7 @@ use strict; use warnings; -use PadWalker 'closed_over'; +use PadWalker 'closed_over', 'set_closed_over'; -print "1..16\n"; +print "1..21\n"; my $x=2; my $h = closed_over (my $sub = sub {my $y = $x++}); @@ -53,7 +53,48 @@ sub bar{ bar(); our $blah = 9; +no warnings 'misc'; my $blah = sub {$blah}; my ($vars, $indices) = closed_over($blah); print (keys %$vars == 0 ? "ok 15\n" : "not ok 15\n"); print (keys %$indices == 0 ? "ok 16\n" : "not ok 16\n"); + + +{ + my $x = 1; + my @foo = (); + my $other = 5; + my $h = closed_over( my $sub = sub { my $y = $x++; push @foo, $y; $y } ); + + my @keys = keys %$h; + + print( @keys == 2 ? "ok 17\n" : "not ok 17\n" ); + print( ${ $h->{'$x'} } eq 1 ? "ok 18\n" : "not ok 18\n" ); + + print( $sub->() == 1 ? "ok 19\n" : "not ok 19\n" ); + + set_closed_over( $sub, { '$x' => \$other } ); + + print( $sub->() == 5 ? "ok 20\n" : "not ok 20\n" ); + + print( $x == 2 ? "ok 21\n" : "not ok 21\n" ); + print( $other == 6 ? "ok 22\n" : "not ok 22\n" ); + + print( @foo == 2 ? "ok 23\n" : "not ok 23\n" ); + + print( $foo[0] == 1 ? "ok 24\n" : "not ok 24\n" ); + + print( $foo[1] == 5 ? "ok 25\n" : "not ok 25\n" ); + + my @other; + + set_closed_over( $sub, { '@foo' => \@other } ); + + print( $sub->() == 6 ? "ok 26\n" : "not ok 26\n" ); + + print( @other == 1 ? "ok 27\n" : "not ok 27\n" ); + + eval { set_closed_over( $sub, { '@foo' => \"foo" } ) }; + + print ( $@ ? "ok 28\n" : "not ok 28\n" ); +} -- 1.6.1.2
Oops, I seem to have mixed tabs/spaces in the test file. I have this fixed in my local repo, but I think I will first add some better error messages before preparing a new patch.