Skip to content

Commit 6a9d009

Browse files
committed
lib/B/Deparse.pm: Don't get upset by OP_METHSTART in method subs
When encountering a `method` sub under `use feature 'class'`, we need to skip over and ignore the `OP_METHSTART` at the beginning, so we can still safely handle the signature ops. We also need to emit the declaration under a `method` keyword, rather than a `sub`.
1 parent 2ec5120 commit 6a9d009

File tree

2 files changed

+40
-12
lines changed

2 files changed

+40
-12
lines changed

lib/B/Deparse.pm

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# This is based on the module of the same name by Malcolm Beattie,
88
# but essentially none of his code remains.
99

10-
package B::Deparse 1.80;
10+
package B::Deparse 1.81;
1111
use strict;
1212
use Carp;
1313
use B qw(class main_root main_start main_cv svref_2object opnumber perlstring
@@ -28,7 +28,7 @@ use B qw(class main_root main_start main_cv svref_2object opnumber perlstring
2828
OPpARG_IF_UNDEF OPpARG_IF_FALSE
2929
SVf_IOK SVf_NOK SVf_ROK SVf_POK SVf_FAKE SVs_RMG SVs_SMG
3030
SVs_PADTMP
31-
CVf_NOWARN_AMBIGUOUS CVf_LVALUE
31+
CVf_NOWARN_AMBIGUOUS CVf_LVALUE CVf_IsMETHOD
3232
PMf_KEEP PMf_GLOBAL PMf_CONTINUE PMf_EVAL PMf_ONCE
3333
PMf_MULTILINE PMf_SINGLELINE PMf_FOLD PMf_EXTENDED PMf_EXTENDED_MORE
3434
PADNAMEf_OUTER PADNAMEf_OUR PADNAMEf_TYPED
@@ -480,7 +480,8 @@ sub next_todo {
480480
# XXX We would do $self->keyword("sub"), but ‘my CORE::sub’
481481
# doesn’t work and ‘my sub’ ignores a &sub in scope. I.e.,
482482
# we have a core bug here.
483-
push @text, "sub " . substr $name->PVX, 1;
483+
my $kw = $cv ? $self->kw_sub_or_method($cv) : "sub";
484+
push @text, "$kw " . substr $name->PVX, 1;
484485
if ($cv) {
485486
# my sub foo { }
486487
push @text, " " . $self->deparse_sub($cv);
@@ -554,7 +555,7 @@ sub next_todo {
554555
} elsif (defined $stash) {
555556
$name =~ s/^\Q$stash\E::(?!\z|.*::)//;
556557
}
557-
my $ret = "$pragmata${p}${l}" . $self->keyword("sub") . " $name "
558+
my $ret = "$pragmata${p}${l}" . $self->keyword($self->kw_sub_or_method($cv)) . " $name "
558559
. $self->deparse_sub($cv);
559560
$self->{'subs_declared'}{$name} = 1;
560561
return $ret;
@@ -1304,6 +1305,12 @@ sub deparse_argops {
13041305
}
13051306

13061307

1308+
sub kw_sub_or_method {
1309+
my $self = shift;
1310+
my $cv = shift;
1311+
return ($cv->CvFLAGS & CVf_IsMETHOD) ? "method" : "sub";
1312+
}
1313+
13071314
# Deparse a sub. Returns everything except the 'sub foo',
13081315
# e.g. ($$) : method { ...; }
13091316
# or : prototype($$) lvalue ($a, $b) { ...; };
@@ -1329,10 +1336,13 @@ Carp::confess("SPECIAL in deparse_sub") if $cv->isa("B::SPECIAL");
13291336
$proto = $myproto;
13301337
}
13311338
}
1332-
if ($cv->CvFLAGS & (CVf_NOWARN_AMBIGUOUS|CVf_LOCKED|CVf_LVALUE|CVf_ANONCONST)) {
1333-
push @attrs, "lvalue" if $cv->CvFLAGS & CVf_LVALUE;
1334-
push @attrs, "method" if $cv->CvFLAGS & CVf_NOWARN_AMBIGUOUS;
1335-
push @attrs, "const" if $cv->CvFLAGS & CVf_ANONCONST;
1339+
my $cv_flags = $cv->CvFLAGS;
1340+
my $is_method = $cv_flags & CVf_IsMETHOD;
1341+
1342+
if ($cv_flags & (CVf_NOWARN_AMBIGUOUS|CVf_LOCKED|CVf_LVALUE|CVf_ANONCONST)) {
1343+
push @attrs, "lvalue" if $cv_flags & CVf_LVALUE;
1344+
push @attrs, "method" if $cv_flags & CVf_NOWARN_AMBIGUOUS and !$is_method;
1345+
push @attrs, "const" if $cv_flags & CVf_ANONCONST;
13361346
}
13371347

13381348
local($self->{'curcv'}) = $cv;
@@ -1351,6 +1361,10 @@ Carp::confess("SPECIAL in deparse_sub") if $cv->isa("B::SPECIAL");
13511361
my $is_list = ($lineseq->name eq "lineseq");
13521362
my $firstop = $is_list ? $lineseq->first : $lineseq;
13531363

1364+
if ($is_method and $firstop->name eq "methstart") {
1365+
$firstop = $firstop->sibling;
1366+
}
1367+
13541368
# Try to deparse first subtree as a signature if possible.
13551369
# Top of signature subtree has an ex-argcheck as a placeholder
13561370
if ( $has_sig
@@ -2812,8 +2826,9 @@ sub pp_refgen {
28122826

28132827
sub e_anoncode {
28142828
my ($self, $info) = @_;
2815-
my $text = $self->deparse_sub($info->{code});
2816-
return $self->keyword("sub") . " $text";
2829+
my $cv = $info->{code};
2830+
my $text = $self->deparse_sub($cv);
2831+
return $self->keyword($self->kw_sub_or_method($cv)) . " $text";
28172832
}
28182833

28192834
sub pp_anoncode {
@@ -5645,7 +5660,7 @@ sub const {
56455660
$self->{curcv}->object_2svref == $ref->object_2svref) {
56465661
return $self->keyword("__SUB__");
56475662
}
5648-
return "sub " . $self->deparse_sub($ref);
5663+
return $self->kw_sub_or_method($ref) . " " . $self->deparse_sub($ref);
56495664
}
56505665
if ($class ne 'SPECIAL' and $ref->FLAGS & SVs_SMG) {
56515666
for (my $mg = $ref->MAGIC; $mg; $mg = $mg->MOREMAGIC) {

lib/B/Deparse.t

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ BEGIN {
1313
use warnings;
1414
use strict;
1515

16-
my $tests = 52; # not counting those in the __DATA__ section
16+
my $tests = 53; # not counting those in the __DATA__ section
1717

1818
use B::Deparse;
1919
my $deparse = B::Deparse->new();
@@ -571,6 +571,19 @@ is runperl(stderr => 1, switches => [ '-MO=-qq,Deparse', $path ],
571571
"package Foo;\nsub f {\n 1;\n}\nsub BEGIN {\n *Bar::f = \\&f;\n}\n",
572572
"sub glob alias in separate package shouldn't impede emitting original sub";
573573

574+
# method declarations (GH#22777)
575+
like runperl(stderr => 1, switches => [ '-MO=-qq,Deparse', $path ],
576+
prog => <<'EOF',
577+
use feature qw( class signatures );
578+
class C {
579+
field $x;
580+
method m () { $x++ }
581+
}
582+
EOF
583+
),
584+
qr/ +method m \(\) \{\n +\$x\+\+;\n +\}/,
585+
"feature class method deparses as method";
586+
574587

575588
done_testing($tests);
576589

0 commit comments

Comments
 (0)