|
| 1 | +#!./perl |
| 2 | + |
| 3 | +BEGIN { |
| 4 | + chdir 't' if -d 't'; |
| 5 | + require "./test.pl"; |
| 6 | + set_up_inc( qw(. ../lib) ); |
| 7 | + require './charset_tools.pl'; |
| 8 | +} |
| 9 | + |
| 10 | +use warnings; |
| 11 | +use strict; |
| 12 | +use Config; |
| 13 | +plan tests => 40; |
| 14 | + |
| 15 | +# Excerpts from 'perldoc -f goto' as of perl-5.40.1 (Aug 2025) |
| 16 | +# |
| 17 | +# The "goto &NAME" form is quite different from the other forms of |
| 18 | +# "goto". In fact, it isn't a goto in the normal sense at all, and |
| 19 | +# doesn't have the stigma associated with other gotos. Instead, it |
| 20 | +# exits the current subroutine (losing any changes set by "local") |
| 21 | +# and immediately calls in its place the named subroutine using |
| 22 | +# the current value of @_. This is used by "AUTOLOAD" subroutines |
| 23 | +# that wish to load another subroutine and then pretend that the |
| 24 | +# other subroutine had been called in the first place (except that |
| 25 | +# any modifications to @_ in the current subroutine are propagated |
| 26 | +# to the other subroutine.) After the "goto", not even "caller" |
| 27 | +# will be able to tell that this routine was called first. |
| 28 | +# |
| 29 | +# NAME needn't be the name of a subroutine; it can be a scalar |
| 30 | +# variable containing a code reference or a block that evaluates |
| 31 | +# to a code reference. |
| 32 | + |
| 33 | +# but earlier, we see: |
| 34 | +# |
| 35 | +# The "goto EXPR" form expects to evaluate "EXPR" to a code |
| 36 | +# reference or a label name. If it evaluates to a code reference, |
| 37 | +# it will be handled like "goto &NAME", below. This is especially |
| 38 | +# useful for implementing tail recursion via "goto __SUB__". |
| 39 | +# |
| 40 | +# The purpose this test file is to consolidate all tests formerly found in |
| 41 | +# t/op/goto.t that exercise the "goto &NAME" functionality. These should be |
| 42 | +# outside the scope of the current (5.42) deprecation of aspects of "goto |
| 43 | +# LABEL" (GH #23618) now scheduled for 5.44. If we have done that |
| 44 | +# successfully, then during the 5.43 dev cycle we shouldn't see any instances |
| 45 | +# of this warning (or of its fatalization replacement). |
| 46 | + |
| 47 | +my $deprecated = 0; |
| 48 | + |
| 49 | +local $SIG{__WARN__} = sub { |
| 50 | + if ($_[0] =~ m/jump into a construct.*?, and will become fatal in Perl 5\.42/) { |
| 51 | + $deprecated++; |
| 52 | + } |
| 53 | + else { warn $_[0] } |
| 54 | +}; |
| 55 | + |
| 56 | +our $foo; |
| 57 | + |
| 58 | +################### |
| 59 | + |
| 60 | +# bug #9990 - don't prematurely free the CV we're &going to. |
| 61 | + |
| 62 | +sub f1 { |
| 63 | + my $x; |
| 64 | + goto sub { $x=0; ok(1,"don't prematurely free CV\n") } |
| 65 | +} |
| 66 | +f1(); |
| 67 | + |
| 68 | +# bug #99850, which is similar - freeing the subroutine we are about to |
| 69 | +# go(in)to during a FREETMPS call should not crash perl. |
| 70 | + |
| 71 | +package _99850 { |
| 72 | + sub reftype{} |
| 73 | + DESTROY { undef &reftype } |
| 74 | + eval { sub { my $guard = bless []; goto &reftype }->() }; |
| 75 | +} |
| 76 | +like $@, qr/^Goto undefined subroutine &_99850::reftype at /, |
| 77 | + 'goto &foo undefining &foo on sub cleanup'; |
| 78 | + |
| 79 | +# When croaking after discovering that the new CV you're about to goto is |
| 80 | +# undef, make sure that the old CV isn't doubly freed. |
| 81 | + |
| 82 | +package Do_undef { |
| 83 | + my $count; |
| 84 | + |
| 85 | + # creating a new closure here encourages any prematurely freed |
| 86 | + # CV to be reallocated |
| 87 | + sub DESTROY { undef &undef_sub; my $x = sub { $count } } |
| 88 | + |
| 89 | + sub f { |
| 90 | + $count++; |
| 91 | + my $guard = bless []; # trigger DESTROY during goto |
| 92 | + *undef_sub = sub {}; |
| 93 | + goto &undef_sub |
| 94 | + } |
| 95 | + |
| 96 | + for (1..10) { |
| 97 | + eval { f() }; |
| 98 | + } |
| 99 | + ::is($count, 10, "goto undef_sub safe"); |
| 100 | +} |
| 101 | + |
| 102 | +# make sure that nothing nasty happens if the old CV is freed while |
| 103 | +# goto'ing |
| 104 | + |
| 105 | +package Free_cv { |
| 106 | + my $results; |
| 107 | + sub f { |
| 108 | + no warnings 'redefine'; |
| 109 | + *f = sub {}; |
| 110 | + goto &g; |
| 111 | + } |
| 112 | + sub g { $results = "(@_)" } |
| 113 | + |
| 114 | + f(1,2,3); |
| 115 | + ::is($results, "(1 2 3)", "Free_cv"); |
| 116 | +} |
| 117 | + |
| 118 | +# [perl #29708] - goto &foo could leave foo() at depth two with |
| 119 | +# @_ == PL_sv_undef, causing a coredump |
| 120 | + |
| 121 | +my $r = runperl( |
| 122 | + prog => |
| 123 | + 'sub f { return if $d; $d=1; my $a=sub {goto &f}; &$a; f() } f(); print qq(ok\n)', |
| 124 | + stderr => 1 |
| 125 | + ); |
| 126 | +is($r, "ok\n", 'avoid pad without an @_'); |
| 127 | + |
| 128 | +# see if a modified @_ propagates |
| 129 | +{ |
| 130 | + my $i; |
| 131 | + package Foo; |
| 132 | + sub DESTROY { my $s = shift; ::is($s->[0], $i, "destroy $i"); } |
| 133 | + sub show { ::is(+@_, 5, "show $i",); } |
| 134 | + sub start { push @_, 1, "foo", {}; goto &show; } |
| 135 | + for (1..3) { $i = $_; start(bless([$_]), 'bar'); } |
| 136 | +} |
| 137 | + |
| 138 | +sub auto { |
| 139 | + goto &loadit; |
| 140 | +} |
| 141 | +my $ok; |
| 142 | + |
| 143 | +sub AUTOLOAD { $ok = 1 if "@_" eq "foo" } |
| 144 | + |
| 145 | +$ok = 0; |
| 146 | +auto("foo"); |
| 147 | +ok($ok, 'autoload'); |
| 148 | + |
| 149 | +# Test autoloading mechanism. |
| 150 | + |
| 151 | +sub two { |
| 152 | + my ($pack, $file, $line) = caller; # Should indicate original call stats. |
| 153 | + is("@_ $pack $file $line", "1 2 3 main $::FILE $::LINE", |
| 154 | + 'autoloading mechanism.'); |
| 155 | +} |
| 156 | + |
| 157 | +sub one { |
| 158 | + eval <<'END'; |
| 159 | + no warnings 'redefine'; |
| 160 | + sub one { pass('sub one'); goto &two; fail('sub one tail'); } |
| 161 | +END |
| 162 | + goto &one; |
| 163 | +} |
| 164 | + |
| 165 | +$::FILE = __FILE__; |
| 166 | +$::LINE = __LINE__ + 1; |
| 167 | +&one(1,2,3); |
| 168 | + |
| 169 | +# deep recursion with gotos eventually caused a stack reallocation |
| 170 | +# which messed up buggy internals that didn't expect the stack to move |
| 171 | + |
| 172 | +sub recurse1 { |
| 173 | + unshift @_, "x"; |
| 174 | + no warnings 'recursion'; |
| 175 | + goto &recurse2; |
| 176 | +} |
| 177 | +sub recurse2 { |
| 178 | + my $x = shift; |
| 179 | + $_[0] ? +1 + recurse1($_[0] - 1) : 0 |
| 180 | +} |
| 181 | + |
| 182 | +{ |
| 183 | +my $w = 0; |
| 184 | + local $SIG{__WARN__} = sub { ++$w }; |
| 185 | + is(recurse1(500), 500, 'recursive goto &foo'); |
| 186 | + is $w, 0, 'no recursion warnings for "no warnings; goto &sub"'; |
| 187 | + delete $SIG{__WARN__}; |
| 188 | +} |
| 189 | + |
| 190 | +# [perl #32039] Chained goto &sub drops data too early. |
| 191 | + |
| 192 | +sub a32039 { @_=("foo"); goto &b32039; } |
| 193 | +sub b32039 { goto &c32039; } |
| 194 | +sub c32039 { is($_[0], 'foo', 'chained &goto') } |
| 195 | +a32039(); |
| 196 | + |
| 197 | +################### |
| 198 | + |
| 199 | +# goto &foo not allowed in evals |
| 200 | + |
| 201 | +sub null { 1 }; |
| 202 | +eval 'goto &null'; |
| 203 | +like($@, qr/Can't goto subroutine from an eval-string/, 'eval string'); |
| 204 | +eval { goto &null }; |
| 205 | +like($@, qr/Can't goto subroutine from an eval-block/, 'eval block'); |
| 206 | + |
| 207 | +# goto &foo leaves @_ alone when called from a sub |
| 208 | +sub returnarg { $_[0] }; |
| 209 | +is sub { |
| 210 | + local *_ = ["ick and queasy"]; |
| 211 | + goto &returnarg; |
| 212 | +}->("quick and easy"), "ick and queasy", |
| 213 | + 'goto &foo with *_{ARRAY} replaced'; |
| 214 | +my @__ = byte_utf8a_to_utf8n("\xc4\x80"); |
| 215 | +sub { local *_ = \@__; goto &utf8::decode }->("no thinking aloud"); |
| 216 | +is "@__", chr 256, 'goto &xsub with replaced *_{ARRAY}'; |
| 217 | + |
| 218 | +# And goto &foo should leave reified @_ alone |
| 219 | +sub { *__ = \@_; goto &null } -> ("rough and tubbery"); |
| 220 | +is ${*__}[0], 'rough and tubbery', 'goto &foo leaves reified @_ alone'; |
| 221 | + |
| 222 | +# goto &xsub when @_ has nonexistent elements |
| 223 | +{ |
| 224 | + no warnings "uninitialized"; |
| 225 | + local @_ = (); |
| 226 | + $#_++; |
| 227 | + & {sub { goto &utf8::encode }}; |
| 228 | + is @_, 1, 'num of elems in @_ after goto &xsub with nonexistent $_[0]'; |
| 229 | + is $_[0], "", 'content of nonexistent $_[0] is modified by goto &xsub'; |
| 230 | +} |
| 231 | + |
| 232 | +# goto &xsub when @_ itself does not exist |
| 233 | +undef *_; |
| 234 | +eval { & { sub { goto &utf8::encode } } }; |
| 235 | +# The main thing we are testing is that it did not crash. But make sure |
| 236 | +# *_{ARRAY} was untouched, too. |
| 237 | +is *_{ARRAY}, undef, 'goto &xsub when @_ does not exist'; |
| 238 | + |
| 239 | +# goto &perlsub when @_ itself does not exist [perl #119949] |
| 240 | +# This was only crashing when the replaced sub call had an argument list. |
| 241 | +# (I.e., &{ sub { goto ... } } did not crash.) |
| 242 | +sub { |
| 243 | + undef *_; |
| 244 | + goto sub { |
| 245 | + is *_{ARRAY}, undef, 'goto &perlsub when @_ does not exist'; |
| 246 | + } |
| 247 | +}->(); |
| 248 | +sub { |
| 249 | + local *_; |
| 250 | + goto sub { |
| 251 | + is *_{ARRAY}, undef, 'goto &sub when @_ does not exist (local *_)'; |
| 252 | + } |
| 253 | +}->(); |
| 254 | + |
| 255 | +# [perl #36521] goto &foo in warn handler could defeat recursion avoider |
| 256 | + |
| 257 | +{ |
| 258 | + my $r = runperl( |
| 259 | + stderr => 1, |
| 260 | + prog => 'my $d; my $w = sub { return if $d++; warn q(bar)}; local $SIG{__WARN__} = sub { goto &$w; }; warn q(foo);' |
| 261 | + ); |
| 262 | + like($r, qr/bar/, "goto &foo in warn"); |
| 263 | +} |
| 264 | + |
| 265 | +{ |
| 266 | + sub TIESCALAR { bless [pop] } |
| 267 | + sub FETCH { $_[0][0] } |
| 268 | + tie my $t, "", sub { "cluck up porridge" }; |
| 269 | + is eval { sub { goto $t }->() }//$@, 'cluck up porridge', |
| 270 | + 'tied arg returning sub ref'; |
| 271 | +} |
| 272 | + |
| 273 | +# v5.31.3-198-gd2cd363728 broke this. goto &XS_sub wasn't restoring |
| 274 | +# cx->blk_sub.old_cxsubix. Would panic in pp_return |
| 275 | + |
| 276 | +{ |
| 277 | + # isa is an XS sub |
| 278 | + sub g198 { goto &UNIVERSAL::isa } |
| 279 | + |
| 280 | + sub f198 { |
| 281 | + g198([], 1 ); |
| 282 | + { |
| 283 | + return 1; |
| 284 | + } |
| 285 | + } |
| 286 | + eval { f198(); }; |
| 287 | + is $@, "", "v5.31.3-198-gd2cd363728"; |
| 288 | +} |
| 289 | + |
| 290 | +# GH #19188 |
| 291 | +# |
| 292 | +# 'goto &xs_sub' should provide the correct caller context to an XS sub |
| 293 | + |
| 294 | +SKIP: |
| 295 | +{ |
| 296 | + skip "No XS::APItest in miniperl", 6 if is_miniperl(); |
| 297 | + skip "No XS::APItest in static perl", 6 if not $Config{usedl}; |
| 298 | + |
| 299 | + require XS::APItest; |
| 300 | + |
| 301 | + sub f_19188 { goto &XS::APItest::gimme } |
| 302 | + sub g_19188{ f_19188(); } |
| 303 | + my ($s, @a); |
| 304 | + |
| 305 | + f_19188(); |
| 306 | + is ($XS::APItest::GIMME_V, 1, 'xs_goto void (#19188)'); |
| 307 | + |
| 308 | + $s = f_19188(); |
| 309 | + is ($XS::APItest::GIMME_V, 2, 'xs_goto scalar (#19188)'); |
| 310 | + |
| 311 | + @a = f_19188(); |
| 312 | + is ($XS::APItest::GIMME_V, 3, 'xs_goto list (#19188)'); |
| 313 | + |
| 314 | + g_19188(); |
| 315 | + is ($XS::APItest::GIMME_V, 1, 'xs_goto indirect void (#19188)'); |
| 316 | + |
| 317 | + $s = g_19188(); |
| 318 | + is ($XS::APItest::GIMME_V, 2, 'xs_goto indirect scalar (#19188)'); |
| 319 | + |
| 320 | + @a = g_19188(); |
| 321 | + is ($XS::APItest::GIMME_V, 3, 'xs_goto indirect list (#19188)'); |
| 322 | +} |
| 323 | + |
| 324 | +# GH #19936 segfault on goto &xs_sub when calling sub is replaced |
| 325 | +SKIP: |
| 326 | +{ |
| 327 | + skip "No XS::APItest in miniperl", 2 if is_miniperl(); |
| 328 | + skip "No XS::APItest in static perl", 2 if not $Config{usedl}; |
| 329 | + |
| 330 | + # utf8::is_utf8() is just an example of an XS sub |
| 331 | + sub foo_19936 { *foo_19936 = {}; goto &utf8::is_utf8 } |
| 332 | + ok(foo_19936("\x{100}"), "GH #19936 utf8 XS call"); |
| 333 | + |
| 334 | + # the gimme XS function accesses PL_op, which was null before the fix |
| 335 | + sub bar_19936 { *bar_19936 = {}; goto &XS::APItest::gimme } |
| 336 | + my @a = bar_19936(); |
| 337 | + is($XS::APItest::GIMME_V, 3, "GH #19936 gimme XS call"); |
| 338 | +} |
| 339 | + |
| 340 | +# goto &sub could leave AvARRAY() slots of @_ uninitialised. |
| 341 | + |
| 342 | +{ |
| 343 | + my $i = 0; |
| 344 | + my $f = sub { |
| 345 | + goto &{ sub {} } unless $i++; |
| 346 | + $_[1] = 1; # create a hole |
| 347 | + # accessing $_[0] is more for valgrind/ASAN to chew on rather than |
| 348 | + # we're too concerned about its value. Or it might give "bizarre |
| 349 | + # copy" errors. |
| 350 | + is($_[0], undef, "goto and AvARRAY"); |
| 351 | + }; |
| 352 | + |
| 353 | + # first call does goto, which gives &$f a fresh AV in pad[0], |
| 354 | + # which formerly allocated an AvARRAY for it, but didn't zero it |
| 355 | + $f->(); |
| 356 | + # second call creates hole in @_ which used to to be a wild SV pointer |
| 357 | + $f->(); |
| 358 | +} |
| 359 | + |
| 360 | + |
| 361 | +# Final test: ensure that we saw no deprecation warnings |
| 362 | +# ... but rework this to count fatalizations once work is more developed |
| 363 | + |
| 364 | +is($deprecated, 0, "No 'jump into a construct' warnings seen"); |
0 commit comments