]> git.vpit.fr Git - perl/modules/Scope-Upper.git/commitdiff
Invalidate the method cache when localizing subroutines
authorVincent Pit <vince@profvince.com>
Mon, 28 Feb 2011 01:11:43 +0000 (02:11 +0100)
committerVincent Pit <vince@profvince.com>
Mon, 28 Feb 2011 01:11:52 +0000 (02:11 +0100)
Upper.xs
t/20-localize-target.t
t/40-localize_delete-target.t

index 999eabaed83fc506825a7dc6659a6934c3e76c0b..f8a35c72449129d26f58cfd96f98ce6df420ae95 100644 (file)
--- 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:
index 40d8f65b160c171afe5cf0d0bcd32e66f32af482..ba77ea88a62b6f7bf677435057de5b66359501d9 100644 (file)
@@ -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/ }
index c5abf9d8bbf7afcf39774c5afb4888adac229fac..044ca60b14c0f9dbed26146da2ccdf67285c1185 100644 (file)
@@ -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/ }