summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gv.h7
-rw-r--r--op.c6
-rw-r--r--scope.c2
-rw-r--r--sv.c2
-rw-r--r--t/mro/method_caching.t32
5 files changed, 44 insertions, 5 deletions
diff --git a/gv.h b/gv.h
index 8e09340053..1e17f35d46 100644
--- a/gv.h
+++ b/gv.h
@@ -264,6 +264,13 @@ Return the CV from the GV.
#define gv_autoload4(stash, name, len, method) \
gv_autoload_pvn(stash, name, len, !!(method))
#define newGVgen(pack) newGVgen_flags(pack, 0)
+#define gv_method_changed(gv) \
+ ( \
+ assert_(isGV_with_GP(gv)) \
+ GvREFCNT(gv) > 1 \
+ ? (void)++PL_sub_generation \
+ : mro_method_changed_in(GvSTASH(gv)) \
+ )
#define gv_AVadd(gv) gv_add_by_type((gv), SVt_PVAV)
#define gv_HVadd(gv) gv_add_by_type((gv), SVt_PVHV)
diff --git a/op.c b/op.c
index 0bc9021e44..c95c8eae4f 100644
--- a/op.c
+++ b/op.c
@@ -7548,7 +7548,7 @@ Perl_newATTRSUB_flags(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs,
GvCVGEN(gv) = 0;
if (HvENAME_HEK(GvSTASH(gv)))
/* sub Foo::bar { (shift)+1 } */
- mro_method_changed_in(GvSTASH(gv));
+ gv_method_changed(gv);
}
}
if (!CvGV(cv)) {
@@ -7872,7 +7872,7 @@ Perl_newXS_len_flags(pTHX_ const char *name, STRLEN len,
GvCV_set(gv,cv);
GvCVGEN(gv) = 0;
if (HvENAME_HEK(GvSTASH(gv)))
- mro_method_changed_in(GvSTASH(gv)); /* newXS */
+ gv_method_changed(gv); /* newXS */
}
}
if (!name)
@@ -7906,7 +7906,7 @@ Perl_newSTUB(pTHX_ GV *gv, bool fake)
GvCV_set(gv, cv);
GvCVGEN(gv) = 0;
if (!fake && HvENAME_HEK(GvSTASH(gv)))
- mro_method_changed_in(GvSTASH(gv));
+ gv_method_changed(gv);
CvGV_set(cv, gv);
CvFILE_set_from_cop(cv, PL_curcop);
CvSTASH_set(cv, PL_curstash);
diff --git a/scope.c b/scope.c
index cd342d05d3..8eca725f22 100644
--- a/scope.c
+++ b/scope.c
@@ -861,7 +861,7 @@ Perl_leave_scope(pTHX_ I32 base)
GvGP_set(gv, (GP*)ptr);
/* putting a method back into circulation ("local")*/
if (GvCVu(gv) && (hv=GvSTASH(gv)) && HvENAME_get(hv))
- mro_method_changed_in(hv);
+ gv_method_changed(gv);
SvREFCNT_dec(gv);
break;
case SAVEt_FREESV:
diff --git a/sv.c b/sv.c
index a2d0cbc204..8570efb401 100644
--- a/sv.c
+++ b/sv.c
@@ -3823,7 +3823,7 @@ S_glob_assign_ref(pTHX_ SV *const dstr, SV *const sstr)
}
GvCVGEN(dstr) = 0; /* Switch off cacheness. */
GvASSUMECV_on(dstr);
- if(GvSTASH(dstr)) mro_method_changed_in(GvSTASH(dstr)); /* sub foo { 1 } sub bar { 2 } *bar = \&foo */
+ if(GvSTASH(dstr)) gv_method_changed(dstr); /* sub foo { 1 } sub bar { 2 } *bar = \&foo */
}
*location = SvREFCNT_inc_simple_NN(sref);
if (import_flag && !(GvFLAGS(dstr) & import_flag)
diff --git a/t/mro/method_caching.t b/t/mro/method_caching.t
index 733193ae1b..d574cc9a22 100644
--- a/t/mro/method_caching.t
+++ b/t/mro/method_caching.t
@@ -1,6 +1,7 @@
#!./perl
use strict;
+no strict 'refs'; # we do a lot of this
use warnings;
no warnings 'redefine'; # we do a lot of this
no warnings 'prototype'; # we do a lot of this
@@ -57,6 +58,37 @@ my @testsubs = (
sub { *{MCTest::Base::} = *{Foo::}; eval { MCTest::Derived->foo(0) }; like($@, qr/locate object method/); },
sub { *MCTest::Derived::foo = \&MCTest::Base::foo; eval { MCTest::Derived::foo(0,0) }; ok(!$@); undef *MCTest::Derived::foo },
sub { eval 'package MCTest::Base; sub foo { $_[1]+18 }'; is(MCTest::Derived->foo(0), 18); },
+
+ # Redefining through a glob alias
+ sub { *A = *{'MCTest::Base::foo'}; eval 'sub A { $_[1]+19 }';
+ is(MCTest::Derived->foo(0), 19,
+ 'redefining sub through glob alias via decl'); },
+ sub { SKIP: {
+ skip_if_miniperl("no XS"); require XS::APItest;
+ *A = *{'MCTest::Base::foo'};
+ XS::APItest::newCONSTSUB(\%main::, "A", 0, 20);
+ is (MCTest::Derived->foo(0), 20,
+ 'redefining sub through glob alias via newXS');
+ } },
+ sub { undef *{'MCTest::Base::foo'}; *A = *{'MCTest::Base::foo'};
+ eval { no warnings 'once'; local *UNIVERSAL::foo = sub {96};
+ MCTest::Derived->foo };
+ ()=\&A;
+ eval { MCTest::Derived->foo };
+ like($@, qr/Undefined subroutine/,
+ 'redefining sub through glob alias via stub vivification'); },
+ sub { *A = *{'MCTest::Base::foo'};
+ local *A = sub { 21 };
+ is(MCTest::Derived->foo, 21,
+ 'redef sub through glob alias via local cv-to-glob assign'); },
+ sub { *A = *{'MCTest::Base::foo'};
+ eval 'sub MCTest::Base::foo { 22 }';
+ { local *A = sub { 23 }; MCTest::Derived->foo }
+ is(MCTest::Derived->foo, 22,
+ 'redef sub through glob alias via localisation unwinding'); },
+ sub { *A = *{'MCTest::Base::foo'}; *A = sub { 24 };
+ is(MCTest::Derived->foo(0), 24,
+ 'redefining sub through glob alias via cv-to-glob assign'); },
);
plan(tests => scalar(@testsubs));