From 65d14c5fff0c1b110fd1f1eabbeb3a3f251b018c Mon Sep 17 00:00:00 2001 From: Tony Cook Date: Thu, 19 Sep 2024 15:28:35 +1000 Subject: [PATCH] signatures: generate only one debug breakable line for signatures With the debugger enabled the argcheck op and each argelem op generated to process a function signature would have an associated dbstate op, allowing the debugger to step on that op. This can be confusing and/or onerous when stepping through code, since for a signature with 7 arguments you'd have 9 steps to get past the prologue of the sub. Make most of these dbstates into nextstates to make them non-steppable. Fixes #22602 --- lib/perl5db.t | 29 +++++++++++++++++++++++++++++ op.c | 3 ++- op.h | 9 +++++++++ perly.act | 8 ++++---- perly.h | 2 +- perly.tab | 2 +- perly.y | 6 +++--- 7 files changed, 49 insertions(+), 10 deletions(-) diff --git a/lib/perl5db.t b/lib/perl5db.t index ab35f993e0dc..76e252c8777a 100644 --- a/lib/perl5db.t +++ b/lib/perl5db.t @@ -3662,6 +3662,35 @@ EOS $wrapper->contents_like(qr/print "2\\n"/, "break immediately after defining problem"); } +{ + my $wrapper = DebugWrap->new( + { + cmds => + [ + "s", + "s", + "s", + "s", + "s", + "s", + "s", + "q", + ], + prog => \<<'EOS', +use v5.40.0; +sub four ($x1, $x2, $x3, $x4) { # start sub four + print $x1, $x2, $x3, $x4; +} +four(1,2,3,4); +EOS + } + ); + my $content = $wrapper->_contents; + my $count = () = $content =~ /start sub four/g; + is($count, 1, "expect to step on sub four entry once") + or diag $content; +} + done_testing(); END { diff --git a/op.c b/op.c index d7fb28f035e6..76dee84167c6 100644 --- a/op.c +++ b/op.c @@ -8896,7 +8896,8 @@ Perl_newSTATEOP(pTHX_ I32 flags, char *label, OP *o) flags &= ~SVf_UTF8; NewOp(1101, cop, 1, COP); - if (PERLDB_LINE && CopLINE(PL_curcop) && PL_curstash != PL_debstash) { + if (PERLDB_LINE && CopLINE(PL_curcop) && PL_curstash != PL_debstash && + !(flags & OPf_FORCE_NEXTSTATE)) { OpTYPE_set(cop, OP_DBSTATE); } else { diff --git a/op.h b/op.h index d64ef04db782..ef0cdd7796a7 100644 --- a/op.h +++ b/op.h @@ -171,6 +171,15 @@ Deprecated. Use C instead. op_convert_list to set op_folded. */ #define OPf_FOLDED (1<<16) +/* In debugging mode newSTATEOP() normally makes an OP_DBSTATE, set + this to force creation of an OP_NEXTSTATE (which isn't + steppable/breakable) instead. + + This is useful if you need to intro_my() but not make a breakable + op. + */ +#define OPf_FORCE_NEXTSTATE (1 << 17) + /* old names; don't use in new code, but don't break them, either */ #define OPf_LIST OPf_WANT_LIST #define OPf_KNOW OPf_WANT diff --git a/perly.act b/perly.act index 386cf3aefe16..e5022e1f930c 100644 --- a/perly.act +++ b/perly.act @@ -945,7 +945,7 @@ case 2: /* @1: %empty */ yyerror("A slurpy parameter may not have " "a default value"); - (yyval.opval) = var ? newSTATEOP(0, NULL, var) : NULL; + (yyval.opval) = var ? newSTATEOP(OPf_FORCE_NEXTSTATE, NULL, var) : NULL; } break; @@ -1024,7 +1024,7 @@ case 2: /* @1: %empty */ "follows optional parameter"); } - (yyval.opval) = var ? newSTATEOP(0, NULL, var) : NULL; + (yyval.opval) = var ? newSTATEOP(OPf_FORCE_NEXTSTATE, NULL, var) : NULL; } break; @@ -1104,7 +1104,7 @@ case 2: /* @1: %empty */ (UNOP_AUX_item *)aux); sigops = op_prepend_elem(OP_LINESEQ, check, sigops); sigops = op_prepend_elem(OP_LINESEQ, - newSTATEOP(0, NULL, NULL), + newSTATEOP(OPf_FORCE_NEXTSTATE, NULL, NULL), sigops); /* a nextstate at the end handles context * correctly for an empty sub body */ @@ -2331,6 +2331,6 @@ case 2: /* @1: %empty */ /* Generated from: - * 9fc11f4af92f701d8d7909a9cd9dc52e01098c42c2504fb84c15e1d1e72f4803 perly.y + * 43bca65a61cd508da43f23047549ee0583ef762d0fa0cff9857d0038ac6eb083 perly.y * f13e9c08cea6302f0c1d1f467405bd0e0880d0ea92d0669901017a7f7e94ab28 regen_perly.pl * ex: set ro ft=c: */ diff --git a/perly.h b/perly.h index 49877e206ea2..6442efb58d59 100644 --- a/perly.h +++ b/perly.h @@ -240,6 +240,6 @@ int yyparse (void); /* Generated from: - * 9fc11f4af92f701d8d7909a9cd9dc52e01098c42c2504fb84c15e1d1e72f4803 perly.y + * 43bca65a61cd508da43f23047549ee0583ef762d0fa0cff9857d0038ac6eb083 perly.y * f13e9c08cea6302f0c1d1f467405bd0e0880d0ea92d0669901017a7f7e94ab28 regen_perly.pl * ex: set ro ft=c: */ diff --git a/perly.tab b/perly.tab index 42292d0eb14b..5c40ee89ca2a 100644 --- a/perly.tab +++ b/perly.tab @@ -1569,6 +1569,6 @@ static const toketypes yy_type_tab[] = }; /* Generated from: - * 9fc11f4af92f701d8d7909a9cd9dc52e01098c42c2504fb84c15e1d1e72f4803 perly.y + * 43bca65a61cd508da43f23047549ee0583ef762d0fa0cff9857d0038ac6eb083 perly.y * f13e9c08cea6302f0c1d1f467405bd0e0880d0ea92d0669901017a7f7e94ab28 regen_perly.pl * ex: set ro ft=c: */ diff --git a/perly.y b/perly.y index 6a6510823f0a..eeddba919395 100644 --- a/perly.y +++ b/perly.y @@ -837,7 +837,7 @@ sigslurpelem: sigslurpsigil sigvarname sigdefault/* def only to catch errors */ yyerror("A slurpy parameter may not have " "a default value"); - $$ = var ? newSTATEOP(0, NULL, var) : NULL; + $$ = var ? newSTATEOP(OPf_FORCE_NEXTSTATE, NULL, var) : NULL; } ; @@ -913,7 +913,7 @@ sigscalarelem: "follows optional parameter"); } - $$ = var ? newSTATEOP(0, NULL, var) : NULL; + $$ = var ? newSTATEOP(OPf_FORCE_NEXTSTATE, NULL, var) : NULL; } ; @@ -985,7 +985,7 @@ subsigguts: (UNOP_AUX_item *)aux); sigops = op_prepend_elem(OP_LINESEQ, check, sigops); sigops = op_prepend_elem(OP_LINESEQ, - newSTATEOP(0, NULL, NULL), + newSTATEOP(OPf_FORCE_NEXTSTATE, NULL, NULL), sigops); /* a nextstate at the end handles context * correctly for an empty sub body */