From: Vincent Pit Date: Mon, 28 Feb 2011 01:11:43 +0000 (+0100) Subject: Invalidate the method cache when localizing subroutines X-Git-Tag: v0.15~8 X-Git-Url: http://git.vpit.fr/?p=perl%2Fmodules%2FScope-Upper.git;a=commitdiff_plain;h=e4175b0fdbe9a877f01c26f2b2a72aa977e75c98 Invalidate the method cache when localizing subroutines --- diff --git a/Upper.xs b/Upper.xs index 999eaba..f8a35c7 100644 --- a/Upper.xs +++ b/Upper.xs @@ -165,7 +165,7 @@ START_MY_CXT # define SU_SAVE_HELEM_OR_HDELETE_SIZE SU_SAVE_HELEM_SIZE #endif -#define SU_SAVE_SPTR_SIZE 3 +#define SU_SAVE_GVCV_SIZE SU_SAVE_DESTRUCTOR_SIZE #if !SU_HAS_PERL(5, 8, 9) # define SU_SAVE_GP_SIZE 6 @@ -318,10 +318,9 @@ STATIC void su_save_helem(pTHX_ HV *hv, SV *keysv, SV *val) { /* ... Saving code slots from a glob ....................................... */ -#if SU_HAS_PERL(5, 13, 10) - -/* Since perl 5.13.10, GvCV() is only a rvalue so we no longer can store a - * pointer to the gvcv member of the gv. */ +#if !SU_HAS_PERL(5, 10, 0) && !defined(mro_method_changed_in) +# define mro_method_changed_in(G) PL_sub_generation++ +#endif typedef struct { GV *gv; @@ -330,14 +329,17 @@ typedef struct { STATIC void su_restore_gvcv(pTHX_ void *ud_) { su_save_gvcv_ud *ud = ud_; + GV *gv = ud->gv; - GvCV_set(ud->gv, ud->old_cv); + GvCV_set(gv, ud->old_cv); + GvCVGEN(gv) = 0; + mro_method_changed_in(GvSTASH(gv)); Safefree(ud); } STATIC void su_save_gvcv(pTHX_ GV *gv) { -#define su_save_gvcv(gv) su_save_gvcv(aTHX_ (gv)) +#define su_save_gvcv(G) su_save_gvcv(aTHX_ (G)) su_save_gvcv_ud *ud; Newx(ud, 1, su_save_gvcv_ud); @@ -345,16 +347,12 @@ STATIC void su_save_gvcv(pTHX_ GV *gv) { ud->old_cv = GvCV(gv); GvCV_set(gv, NULL); + GvCVGEN(gv) = 0; + mro_method_changed_in(GvSTASH(gv)); SAVEDESTRUCTOR_X(su_restore_gvcv, ud); } -#else - -#define su_save_gvcv(gv) SAVESPTR(GvCV(gv)), GvCV_set((gv), NULL) - -#endif - /* --- Actions ------------------------------------------------------------- */ typedef struct { @@ -520,7 +518,7 @@ STATIC I32 su_ud_localize_init(pTHX_ su_ud_localize *ud, SV *sv, SV *val, SV *el deref = 0; break; case SVt_PVCV: - size = SU_SAVE_SPTR_SIZE; + size = SU_SAVE_GVCV_SIZE; deref = 0; break; default: diff --git a/t/20-localize-target.t b/t/20-localize-target.t index 40d8f65..ba77ea8 100644 --- a/t/20-localize-target.t +++ b/t/20-localize-target.t @@ -3,7 +3,7 @@ use strict; use warnings; -use Test::More tests => 61 + 4; +use Test::More tests => 70 + 4; use Scope::Upper qw/localize UP HERE/; @@ -294,6 +294,43 @@ my $xh = { a => 5, c => 7 }; is foo(), 'x', 'localize *foo, sub { "y" } => UP [end]'; } +sub X::foo { 'X::foo' } + +{ + { + { + localize 'X::foo', sub { 'X::foo 2' } => UP; + is(X->foo, 'X::foo', 'localize "X::foo", sub { "X::foo 2" } => UP [not yet]') + } + is(X->foo, 'X::foo 2', 'localize "X::foo", sub { "X::foo 2" } => UP [ok]'); + } + is(X->foo, 'X::foo', 'localize "X::foo", sub { "X::foo 2" } => UP [end]'); +} + +@Y::ISA = 'X'; + +{ + { + { + localize 'X::foo', sub { 'X::foo 3' } => UP; + is(Y->foo, 'X::foo', 'localize "X::foo", sub { "X::foo 3" } => UP [not yet]') + } + is(Y->foo, 'X::foo 3', 'localize "X::foo", sub { "X::foo 3" } => UP [ok]'); + } + is(Y->foo, 'X::foo', 'localize "X::foo", sub { "X::foo 2" } => UP [end]'); +} + +{ + { + { + localize 'Y::foo', sub { 'Y::foo' } => UP; + is(Y->foo, 'X::foo', 'localize "Y::foo", sub { "Y::foo" } => UP [not yet]'); + } + is(Y->foo, 'Y::foo', 'localize "Y::foo", sub { "Y::foo" } => UP [ok]'); + } + is(Y->foo, 'X::foo', 'localize "Y::foo", sub { "Y::foo" } => UP [end]'); +} + # Invalid sub invalid_ref { qr/^Invalid \Q$_[0]\E reference as the localization target/ } diff --git a/t/40-localize_delete-target.t b/t/40-localize_delete-target.t index c5abf9d..044ca60 100644 --- a/t/40-localize_delete-target.t +++ b/t/40-localize_delete-target.t @@ -3,7 +3,7 @@ use strict; use warnings; -use Test::More tests => 44 + 4; +use Test::More tests => 53 + 4; use Scope::Upper qw/localize_delete UP HERE/; @@ -162,7 +162,7 @@ our %h; 'localize_delete "%nonexistent", anything => HERE [end]'; } -# Others +# Scalars our $x = 1; { @@ -181,6 +181,8 @@ is $x, 1, 'localize_delete "$x", anything => HERE [end]'; 'localize_delete "$nonexistent", anything => HERE [end]'; } +# Code + sub x { 1 }; { localize_delete '&x', 2 => HERE; @@ -206,6 +208,50 @@ is x(), 1, 'localize_delete "&x", anything => HERE [end]'; is x(), 1, 'localize_delete *x, anything => HERE [end 1]'; is $x, 1, 'localize_delete *x, anything => HERE [end 2]'; +sub X::foo { 'X::foo' } + +{ + { + { + localize_delete '&X::foo', undef => UP; + is(X->foo(), 'X::foo', 'localize_delete "&X::foo", undef => UP [not yet X]'); + } + ok(!X->can('foo'), 'localize_delete "&X::foo", undef => UP [ok X]'); + } + is(X->foo(), 'X::foo', 'localize_delete "&X::foo", undef => UP [end X]'); +} + +@Y::ISA = 'X'; + +{ + { + { + localize_delete '&X::foo', undef => UP; + is(Y->foo(), 'X::foo', 'localize_delete "&X::foo", undef => UP [not yet Y]'); + } + ok(!Y->can('foo'), 'localize_delete "&X::foo", undef => UP [ok Y]'); + } + is(Y->foo(), 'X::foo', 'localize_delete "&X::foo", undef => UP [end Y]'); +} + + +{ + local *Y::foo = sub { 'Y::foo' }; + { + { + localize_delete '&Y::foo', undef => UP; + is(Y->foo(), 'Y::foo', 'localize_delete "&Y::foo", undef => UP [not yet]'); + } + is(Y->foo(), 'X::foo', 'localize_delete "&Y::foo", undef => UP [ok]'); + } + is(Y->foo(), 'Y::foo', 'localize_delete "&Y::foo", undef => UP [end]'); +} + +{ + # Prevent 'only once' warnings + local *Y::foo = *Y::foo; +} + # Invalid sub invalid_ref { qr/^Invalid \Q$_[0]\E reference as the localization target/ }