| 1 | #!./perl
|
|---|
| 2 | # -*- Mode: Perl -*-
|
|---|
| 3 | # closure.t:
|
|---|
| 4 | # Original written by Ulrich Pfeifer on 2 Jan 1997.
|
|---|
| 5 | # Greatly extended by Tom Phoenix <rootbeer@teleport.com> on 28 Jan 1997.
|
|---|
| 6 | #
|
|---|
| 7 | # Run with -debug for debugging output.
|
|---|
| 8 |
|
|---|
| 9 | BEGIN {
|
|---|
| 10 | chdir 't' if -d 't';
|
|---|
| 11 | @INC = '../lib';
|
|---|
| 12 | }
|
|---|
| 13 |
|
|---|
| 14 | use Config;
|
|---|
| 15 | require './test.pl'; # for runperl()
|
|---|
| 16 |
|
|---|
| 17 | print "1..187\n";
|
|---|
| 18 |
|
|---|
| 19 | my $test = 1;
|
|---|
| 20 | sub test (&) {
|
|---|
| 21 | my $ok = &{$_[0]};
|
|---|
| 22 | print $ok ? "ok $test\n" : "not ok $test\n";
|
|---|
| 23 | printf "# Failed at line %d\n", (caller)[2] unless $ok;
|
|---|
| 24 | $test++;
|
|---|
| 25 | }
|
|---|
| 26 |
|
|---|
| 27 | my $i = 1;
|
|---|
| 28 | sub foo { $i = shift if @_; $i }
|
|---|
| 29 |
|
|---|
| 30 | # no closure
|
|---|
| 31 | test { foo == 1 };
|
|---|
| 32 | foo(2);
|
|---|
| 33 | test { foo == 2 };
|
|---|
| 34 |
|
|---|
| 35 | # closure: lexical outside sub
|
|---|
| 36 | my $foo = sub {$i = shift if @_; $i };
|
|---|
| 37 | my $bar = sub {$i = shift if @_; $i };
|
|---|
| 38 | test {&$foo() == 2 };
|
|---|
| 39 | &$foo(3);
|
|---|
| 40 | test {&$foo() == 3 };
|
|---|
| 41 | # did the lexical change?
|
|---|
| 42 | test { foo == 3 and $i == 3};
|
|---|
| 43 | # did the second closure notice?
|
|---|
| 44 | test {&$bar() == 3 };
|
|---|
| 45 |
|
|---|
| 46 | # closure: lexical inside sub
|
|---|
| 47 | sub bar {
|
|---|
| 48 | my $i = shift;
|
|---|
| 49 | sub { $i = shift if @_; $i }
|
|---|
| 50 | }
|
|---|
| 51 |
|
|---|
| 52 | $foo = bar(4);
|
|---|
| 53 | $bar = bar(5);
|
|---|
| 54 | test {&$foo() == 4 };
|
|---|
| 55 | &$foo(6);
|
|---|
| 56 | test {&$foo() == 6 };
|
|---|
| 57 | test {&$bar() == 5 };
|
|---|
| 58 |
|
|---|
| 59 | # nested closures
|
|---|
| 60 | sub bizz {
|
|---|
| 61 | my $i = 7;
|
|---|
| 62 | if (@_) {
|
|---|
| 63 | my $i = shift;
|
|---|
| 64 | sub {$i = shift if @_; $i };
|
|---|
| 65 | } else {
|
|---|
| 66 | my $i = $i;
|
|---|
| 67 | sub {$i = shift if @_; $i };
|
|---|
| 68 | }
|
|---|
| 69 | }
|
|---|
| 70 | $foo = bizz();
|
|---|
| 71 | $bar = bizz();
|
|---|
| 72 | test {&$foo() == 7 };
|
|---|
| 73 | &$foo(8);
|
|---|
| 74 | test {&$foo() == 8 };
|
|---|
| 75 | test {&$bar() == 7 };
|
|---|
| 76 |
|
|---|
| 77 | $foo = bizz(9);
|
|---|
| 78 | $bar = bizz(10);
|
|---|
| 79 | test {&$foo(11)-1 == &$bar()};
|
|---|
| 80 |
|
|---|
| 81 | my @foo;
|
|---|
| 82 | for (qw(0 1 2 3 4)) {
|
|---|
| 83 | my $i = $_;
|
|---|
| 84 | $foo[$_] = sub {$i = shift if @_; $i };
|
|---|
| 85 | }
|
|---|
| 86 |
|
|---|
| 87 | test {
|
|---|
| 88 | &{$foo[0]}() == 0 and
|
|---|
| 89 | &{$foo[1]}() == 1 and
|
|---|
| 90 | &{$foo[2]}() == 2 and
|
|---|
| 91 | &{$foo[3]}() == 3 and
|
|---|
| 92 | &{$foo[4]}() == 4
|
|---|
| 93 | };
|
|---|
| 94 |
|
|---|
| 95 | for (0 .. 4) {
|
|---|
| 96 | &{$foo[$_]}(4-$_);
|
|---|
| 97 | }
|
|---|
| 98 |
|
|---|
| 99 | test {
|
|---|
| 100 | &{$foo[0]}() == 4 and
|
|---|
| 101 | &{$foo[1]}() == 3 and
|
|---|
| 102 | &{$foo[2]}() == 2 and
|
|---|
| 103 | &{$foo[3]}() == 1 and
|
|---|
| 104 | &{$foo[4]}() == 0
|
|---|
| 105 | };
|
|---|
| 106 |
|
|---|
| 107 | sub barf {
|
|---|
| 108 | my @foo;
|
|---|
| 109 | for (qw(0 1 2 3 4)) {
|
|---|
| 110 | my $i = $_;
|
|---|
| 111 | $foo[$_] = sub {$i = shift if @_; $i };
|
|---|
| 112 | }
|
|---|
| 113 | @foo;
|
|---|
| 114 | }
|
|---|
| 115 |
|
|---|
| 116 | @foo = barf();
|
|---|
| 117 | test {
|
|---|
| 118 | &{$foo[0]}() == 0 and
|
|---|
| 119 | &{$foo[1]}() == 1 and
|
|---|
| 120 | &{$foo[2]}() == 2 and
|
|---|
| 121 | &{$foo[3]}() == 3 and
|
|---|
| 122 | &{$foo[4]}() == 4
|
|---|
| 123 | };
|
|---|
| 124 |
|
|---|
| 125 | for (0 .. 4) {
|
|---|
| 126 | &{$foo[$_]}(4-$_);
|
|---|
| 127 | }
|
|---|
| 128 |
|
|---|
| 129 | test {
|
|---|
| 130 | &{$foo[0]}() == 4 and
|
|---|
| 131 | &{$foo[1]}() == 3 and
|
|---|
| 132 | &{$foo[2]}() == 2 and
|
|---|
| 133 | &{$foo[3]}() == 1 and
|
|---|
| 134 | &{$foo[4]}() == 0
|
|---|
| 135 | };
|
|---|
| 136 |
|
|---|
| 137 | # test if closures get created in optimized for loops
|
|---|
| 138 |
|
|---|
| 139 | my %foo;
|
|---|
| 140 | for my $n ('A'..'E') {
|
|---|
| 141 | $foo{$n} = sub { $n eq $_[0] };
|
|---|
| 142 | }
|
|---|
| 143 |
|
|---|
| 144 | test {
|
|---|
| 145 | &{$foo{A}}('A') and
|
|---|
| 146 | &{$foo{B}}('B') and
|
|---|
| 147 | &{$foo{C}}('C') and
|
|---|
| 148 | &{$foo{D}}('D') and
|
|---|
| 149 | &{$foo{E}}('E')
|
|---|
| 150 | };
|
|---|
| 151 |
|
|---|
| 152 | for my $n (0..4) {
|
|---|
| 153 | $foo[$n] = sub { $n == $_[0] };
|
|---|
| 154 | }
|
|---|
| 155 |
|
|---|
| 156 | test {
|
|---|
| 157 | &{$foo[0]}(0) and
|
|---|
| 158 | &{$foo[1]}(1) and
|
|---|
| 159 | &{$foo[2]}(2) and
|
|---|
| 160 | &{$foo[3]}(3) and
|
|---|
| 161 | &{$foo[4]}(4)
|
|---|
| 162 | };
|
|---|
| 163 |
|
|---|
| 164 | for my $n (0..4) {
|
|---|
| 165 | $foo[$n] = sub {
|
|---|
| 166 | # no intervening reference to $n here
|
|---|
| 167 | sub { $n == $_[0] }
|
|---|
| 168 | };
|
|---|
| 169 | }
|
|---|
| 170 |
|
|---|
| 171 | test {
|
|---|
| 172 | $foo[0]->()->(0) and
|
|---|
| 173 | $foo[1]->()->(1) and
|
|---|
| 174 | $foo[2]->()->(2) and
|
|---|
| 175 | $foo[3]->()->(3) and
|
|---|
| 176 | $foo[4]->()->(4)
|
|---|
| 177 | };
|
|---|
| 178 |
|
|---|
| 179 | {
|
|---|
| 180 | my $w;
|
|---|
| 181 | $w = sub {
|
|---|
| 182 | my ($i) = @_;
|
|---|
| 183 | test { $i == 10 };
|
|---|
| 184 | sub { $w };
|
|---|
| 185 | };
|
|---|
| 186 | $w->(10);
|
|---|
| 187 | }
|
|---|
| 188 |
|
|---|
| 189 | # Additional tests by Tom Phoenix <rootbeer@teleport.com>.
|
|---|
| 190 |
|
|---|
| 191 | {
|
|---|
| 192 | use strict;
|
|---|
| 193 |
|
|---|
| 194 | use vars qw!$test!;
|
|---|
| 195 | my($debugging, %expected, $inner_type, $where_declared, $within);
|
|---|
| 196 | my($nc_attempt, $call_outer, $call_inner, $undef_outer);
|
|---|
| 197 | my($code, $inner_sub_test, $expected, $line, $errors, $output);
|
|---|
| 198 | my(@inners, $sub_test, $pid);
|
|---|
| 199 | $debugging = 1 if defined($ARGV[0]) and $ARGV[0] eq '-debug';
|
|---|
| 200 |
|
|---|
| 201 | # The expected values for these tests
|
|---|
| 202 | %expected = (
|
|---|
| 203 | 'global_scalar' => 1001,
|
|---|
| 204 | 'global_array' => 2101,
|
|---|
| 205 | 'global_hash' => 3004,
|
|---|
| 206 | 'fs_scalar' => 4001,
|
|---|
| 207 | 'fs_array' => 5101,
|
|---|
| 208 | 'fs_hash' => 6004,
|
|---|
| 209 | 'sub_scalar' => 7001,
|
|---|
| 210 | 'sub_array' => 8101,
|
|---|
| 211 | 'sub_hash' => 9004,
|
|---|
| 212 | 'foreach' => 10011,
|
|---|
| 213 | );
|
|---|
| 214 |
|
|---|
| 215 | # Our innermost sub is either named or anonymous
|
|---|
| 216 | for $inner_type (qw!named anon!) {
|
|---|
| 217 | # And it may be declared at filescope, within a named
|
|---|
| 218 | # sub, or within an anon sub
|
|---|
| 219 | for $where_declared (qw!filescope in_named in_anon!) {
|
|---|
| 220 | # And that, in turn, may be within a foreach loop,
|
|---|
| 221 | # a naked block, or another named sub
|
|---|
| 222 | for $within (qw!foreach naked other_sub!) {
|
|---|
| 223 |
|
|---|
| 224 | # Here are a number of variables which show what's
|
|---|
| 225 | # going on, in a way.
|
|---|
| 226 | $nc_attempt = 0+ # Named closure attempted
|
|---|
| 227 | ( ($inner_type eq 'named') ||
|
|---|
| 228 | ($within eq 'other_sub') ) ;
|
|---|
| 229 | $call_inner = 0+ # Need to call &inner
|
|---|
| 230 | ( ($inner_type eq 'anon') &&
|
|---|
| 231 | ($within eq 'other_sub') ) ;
|
|---|
| 232 | $call_outer = 0+ # Need to call &outer or &$outer
|
|---|
| 233 | ( ($inner_type eq 'anon') &&
|
|---|
| 234 | ($within ne 'other_sub') ) ;
|
|---|
| 235 | $undef_outer = 0+ # $outer is created but unused
|
|---|
| 236 | ( ($where_declared eq 'in_anon') &&
|
|---|
| 237 | (not $call_outer) ) ;
|
|---|
| 238 |
|
|---|
| 239 | $code = "# This is a test script built by t/op/closure.t\n\n";
|
|---|
| 240 |
|
|---|
| 241 | print <<"DEBUG_INFO" if $debugging;
|
|---|
| 242 | # inner_type: $inner_type
|
|---|
| 243 | # where_declared: $where_declared
|
|---|
| 244 | # within: $within
|
|---|
| 245 | # nc_attempt: $nc_attempt
|
|---|
| 246 | # call_inner: $call_inner
|
|---|
| 247 | # call_outer: $call_outer
|
|---|
| 248 | # undef_outer: $undef_outer
|
|---|
| 249 | DEBUG_INFO
|
|---|
| 250 |
|
|---|
| 251 | $code .= <<"END_MARK_ONE";
|
|---|
| 252 |
|
|---|
| 253 | BEGIN { \$SIG{__WARN__} = sub {
|
|---|
| 254 | my \$msg = \$_[0];
|
|---|
| 255 | END_MARK_ONE
|
|---|
| 256 |
|
|---|
| 257 | $code .= <<"END_MARK_TWO" if $nc_attempt;
|
|---|
| 258 | return if index(\$msg, 'will not stay shared') != -1;
|
|---|
| 259 | return if index(\$msg, 'may be unavailable') != -1;
|
|---|
| 260 | END_MARK_TWO
|
|---|
| 261 |
|
|---|
| 262 | $code .= <<"END_MARK_THREE"; # Backwhack a lot!
|
|---|
| 263 | print "not ok: got unexpected warning \$msg\\n";
|
|---|
| 264 | } }
|
|---|
| 265 |
|
|---|
| 266 | {
|
|---|
| 267 | my \$test = $test;
|
|---|
| 268 | sub test (&) {
|
|---|
| 269 | my \$ok = &{\$_[0]};
|
|---|
| 270 | print \$ok ? "ok \$test\n" : "not ok \$test\n";
|
|---|
| 271 | printf "# Failed at line %d\n", (caller)[2] unless \$ok;
|
|---|
| 272 | \$test++;
|
|---|
| 273 | }
|
|---|
| 274 | }
|
|---|
| 275 |
|
|---|
| 276 | # some of the variables which the closure will access
|
|---|
| 277 | \$global_scalar = 1000;
|
|---|
| 278 | \@global_array = (2000, 2100, 2200, 2300);
|
|---|
| 279 | %global_hash = 3000..3009;
|
|---|
| 280 |
|
|---|
| 281 | my \$fs_scalar = 4000;
|
|---|
| 282 | my \@fs_array = (5000, 5100, 5200, 5300);
|
|---|
| 283 | my %fs_hash = 6000..6009;
|
|---|
| 284 |
|
|---|
| 285 | END_MARK_THREE
|
|---|
| 286 |
|
|---|
| 287 | if ($where_declared eq 'filescope') {
|
|---|
| 288 | # Nothing here
|
|---|
| 289 | } elsif ($where_declared eq 'in_named') {
|
|---|
| 290 | $code .= <<'END';
|
|---|
| 291 | sub outer {
|
|---|
| 292 | my $sub_scalar = 7000;
|
|---|
| 293 | my @sub_array = (8000, 8100, 8200, 8300);
|
|---|
| 294 | my %sub_hash = 9000..9009;
|
|---|
| 295 | END
|
|---|
| 296 | # }
|
|---|
| 297 | } elsif ($where_declared eq 'in_anon') {
|
|---|
| 298 | $code .= <<'END';
|
|---|
| 299 | $outer = sub {
|
|---|
| 300 | my $sub_scalar = 7000;
|
|---|
| 301 | my @sub_array = (8000, 8100, 8200, 8300);
|
|---|
| 302 | my %sub_hash = 9000..9009;
|
|---|
| 303 | END
|
|---|
| 304 | # }
|
|---|
| 305 | } else {
|
|---|
| 306 | die "What was $where_declared?"
|
|---|
| 307 | }
|
|---|
| 308 |
|
|---|
| 309 | if ($within eq 'foreach') {
|
|---|
| 310 | $code .= "
|
|---|
| 311 | my \$foreach = 12000;
|
|---|
| 312 | my \@list = (10000, 10010);
|
|---|
| 313 | foreach \$foreach (\@list) {
|
|---|
| 314 | " # }
|
|---|
| 315 | } elsif ($within eq 'naked') {
|
|---|
| 316 | $code .= " { # naked block\n" # }
|
|---|
| 317 | } elsif ($within eq 'other_sub') {
|
|---|
| 318 | $code .= " sub inner_sub {\n" # }
|
|---|
| 319 | } else {
|
|---|
| 320 | die "What was $within?"
|
|---|
| 321 | }
|
|---|
| 322 |
|
|---|
| 323 | $sub_test = $test;
|
|---|
| 324 | @inners = ( qw!global_scalar global_array global_hash! ,
|
|---|
| 325 | qw!fs_scalar fs_array fs_hash! );
|
|---|
| 326 | push @inners, 'foreach' if $within eq 'foreach';
|
|---|
| 327 | if ($where_declared ne 'filescope') {
|
|---|
| 328 | push @inners, qw!sub_scalar sub_array sub_hash!;
|
|---|
| 329 | }
|
|---|
| 330 | for $inner_sub_test (@inners) {
|
|---|
| 331 |
|
|---|
| 332 | if ($inner_type eq 'named') {
|
|---|
| 333 | $code .= " sub named_$sub_test "
|
|---|
| 334 | } elsif ($inner_type eq 'anon') {
|
|---|
| 335 | $code .= " \$anon_$sub_test = sub "
|
|---|
| 336 | } else {
|
|---|
| 337 | die "What was $inner_type?"
|
|---|
| 338 | }
|
|---|
| 339 |
|
|---|
| 340 | # Now to write the body of the test sub
|
|---|
| 341 | if ($inner_sub_test eq 'global_scalar') {
|
|---|
| 342 | $code .= '{ ++$global_scalar }'
|
|---|
| 343 | } elsif ($inner_sub_test eq 'fs_scalar') {
|
|---|
| 344 | $code .= '{ ++$fs_scalar }'
|
|---|
| 345 | } elsif ($inner_sub_test eq 'sub_scalar') {
|
|---|
| 346 | $code .= '{ ++$sub_scalar }'
|
|---|
| 347 | } elsif ($inner_sub_test eq 'global_array') {
|
|---|
| 348 | $code .= '{ ++$global_array[1] }'
|
|---|
| 349 | } elsif ($inner_sub_test eq 'fs_array') {
|
|---|
| 350 | $code .= '{ ++$fs_array[1] }'
|
|---|
| 351 | } elsif ($inner_sub_test eq 'sub_array') {
|
|---|
| 352 | $code .= '{ ++$sub_array[1] }'
|
|---|
| 353 | } elsif ($inner_sub_test eq 'global_hash') {
|
|---|
| 354 | $code .= '{ ++$global_hash{3002} }'
|
|---|
| 355 | } elsif ($inner_sub_test eq 'fs_hash') {
|
|---|
| 356 | $code .= '{ ++$fs_hash{6002} }'
|
|---|
| 357 | } elsif ($inner_sub_test eq 'sub_hash') {
|
|---|
| 358 | $code .= '{ ++$sub_hash{9002} }'
|
|---|
| 359 | } elsif ($inner_sub_test eq 'foreach') {
|
|---|
| 360 | $code .= '{ ++$foreach }'
|
|---|
| 361 | } else {
|
|---|
| 362 | die "What was $inner_sub_test?"
|
|---|
| 363 | }
|
|---|
| 364 |
|
|---|
| 365 | # Close up
|
|---|
| 366 | if ($inner_type eq 'anon') {
|
|---|
| 367 | $code .= ';'
|
|---|
| 368 | }
|
|---|
| 369 | $code .= "\n";
|
|---|
| 370 | $sub_test++; # sub name sequence number
|
|---|
| 371 |
|
|---|
| 372 | } # End of foreach $inner_sub_test
|
|---|
| 373 |
|
|---|
| 374 | # Close up $within block # {
|
|---|
| 375 | $code .= " }\n\n";
|
|---|
| 376 |
|
|---|
| 377 | # Close up $where_declared block
|
|---|
| 378 | if ($where_declared eq 'in_named') { # {
|
|---|
| 379 | $code .= "}\n\n";
|
|---|
| 380 | } elsif ($where_declared eq 'in_anon') { # {
|
|---|
| 381 | $code .= "};\n\n";
|
|---|
| 382 | }
|
|---|
| 383 |
|
|---|
| 384 | # We may need to do something with the sub we just made...
|
|---|
| 385 | $code .= "undef \$outer;\n" if $undef_outer;
|
|---|
| 386 | $code .= "&inner_sub;\n" if $call_inner;
|
|---|
| 387 | if ($call_outer) {
|
|---|
| 388 | if ($where_declared eq 'in_named') {
|
|---|
| 389 | $code .= "&outer;\n\n";
|
|---|
| 390 | } elsif ($where_declared eq 'in_anon') {
|
|---|
| 391 | $code .= "&\$outer;\n\n"
|
|---|
| 392 | }
|
|---|
| 393 | }
|
|---|
| 394 |
|
|---|
| 395 | # Now, we can actually prep to run the tests.
|
|---|
| 396 | for $inner_sub_test (@inners) {
|
|---|
| 397 | $expected = $expected{$inner_sub_test} or
|
|---|
| 398 | die "expected $inner_sub_test missing";
|
|---|
| 399 |
|
|---|
| 400 | # Named closures won't access the expected vars
|
|---|
| 401 | if ( $nc_attempt and
|
|---|
| 402 | substr($inner_sub_test, 0, 4) eq "sub_" ) {
|
|---|
| 403 | $expected = 1;
|
|---|
| 404 | }
|
|---|
| 405 |
|
|---|
| 406 | # If you make a sub within a foreach loop,
|
|---|
| 407 | # what happens if it tries to access the
|
|---|
| 408 | # foreach index variable? If it's a named
|
|---|
| 409 | # sub, it gets the var from "outside" the loop,
|
|---|
| 410 | # but if it's anon, it gets the value to which
|
|---|
| 411 | # the index variable is aliased.
|
|---|
| 412 | #
|
|---|
| 413 | # Of course, if the value was set only
|
|---|
| 414 | # within another sub which was never called,
|
|---|
| 415 | # the value has not been set yet.
|
|---|
| 416 | #
|
|---|
| 417 | if ($inner_sub_test eq 'foreach') {
|
|---|
| 418 | if ($inner_type eq 'named') {
|
|---|
| 419 | if ($call_outer || ($where_declared eq 'filescope')) {
|
|---|
| 420 | $expected = 12001
|
|---|
| 421 | } else {
|
|---|
| 422 | $expected = 1
|
|---|
| 423 | }
|
|---|
| 424 | }
|
|---|
| 425 | }
|
|---|
| 426 |
|
|---|
| 427 | # Here's the test:
|
|---|
| 428 | if ($inner_type eq 'anon') {
|
|---|
| 429 | $code .= "test { &\$anon_$test == $expected };\n"
|
|---|
| 430 | } else {
|
|---|
| 431 | $code .= "test { &named_$test == $expected };\n"
|
|---|
| 432 | }
|
|---|
| 433 | $test++;
|
|---|
| 434 | }
|
|---|
| 435 |
|
|---|
| 436 | if ($Config{d_fork} and $^O ne 'VMS' and $^O ne 'MSWin32' and $^O ne 'NetWare') {
|
|---|
| 437 | # Fork off a new perl to run the tests.
|
|---|
| 438 | # (This is so we can catch spurious warnings.)
|
|---|
| 439 | $| = 1; print ""; $| = 0; # flush output before forking
|
|---|
| 440 | pipe READ, WRITE or die "Can't make pipe: $!";
|
|---|
| 441 | pipe READ2, WRITE2 or die "Can't make second pipe: $!";
|
|---|
| 442 | die "Can't fork: $!" unless defined($pid = open PERL, "|-");
|
|---|
| 443 | unless ($pid) {
|
|---|
| 444 | # Child process here. We're going to send errors back
|
|---|
| 445 | # through the extra pipe.
|
|---|
| 446 | close READ;
|
|---|
| 447 | close READ2;
|
|---|
| 448 | open STDOUT, ">&WRITE" or die "Can't redirect STDOUT: $!";
|
|---|
| 449 | open STDERR, ">&WRITE2" or die "Can't redirect STDERR: $!";
|
|---|
| 450 | exec which_perl(), '-w', '-'
|
|---|
| 451 | or die "Can't exec perl: $!";
|
|---|
| 452 | } else {
|
|---|
| 453 | # Parent process here.
|
|---|
| 454 | close WRITE;
|
|---|
| 455 | close WRITE2;
|
|---|
| 456 | print PERL $code;
|
|---|
| 457 | close PERL;
|
|---|
| 458 | { local $/;
|
|---|
| 459 | $output = join '', <READ>;
|
|---|
| 460 | $errors = join '', <READ2>; }
|
|---|
| 461 | close READ;
|
|---|
| 462 | close READ2;
|
|---|
| 463 | }
|
|---|
| 464 | } else {
|
|---|
| 465 | # No fork(). Do it the hard way.
|
|---|
| 466 | my $cmdfile = "tcmd$$"; $cmdfile++ while -e $cmdfile;
|
|---|
| 467 | my $errfile = "terr$$"; $errfile++ while -e $errfile;
|
|---|
| 468 | my @tmpfiles = ($cmdfile, $errfile);
|
|---|
| 469 | open CMD, ">$cmdfile"; print CMD $code; close CMD;
|
|---|
| 470 | my $cmd = which_perl();
|
|---|
| 471 | $cmd .= " -w $cmdfile 2>$errfile";
|
|---|
| 472 | if ($^O eq 'VMS' or $^O eq 'MSWin32' or $^O eq 'NetWare') {
|
|---|
| 473 | # Use pipe instead of system so we don't inherit STD* from
|
|---|
| 474 | # this process, and then foul our pipe back to parent by
|
|---|
| 475 | # redirecting output in the child.
|
|---|
| 476 | open PERL,"$cmd |" or die "Can't open pipe: $!\n";
|
|---|
| 477 | { local $/; $output = join '', <PERL> }
|
|---|
| 478 | close PERL;
|
|---|
| 479 | } else {
|
|---|
| 480 | my $outfile = "tout$$"; $outfile++ while -e $outfile;
|
|---|
| 481 | push @tmpfiles, $outfile;
|
|---|
| 482 | system "$cmd >$outfile";
|
|---|
| 483 | { local $/; open IN, $outfile; $output = <IN>; close IN }
|
|---|
| 484 | }
|
|---|
| 485 | if ($?) {
|
|---|
| 486 | printf "not ok: exited with error code %04X\n", $?;
|
|---|
| 487 | $debugging or do { 1 while unlink @tmpfiles };
|
|---|
| 488 | exit;
|
|---|
| 489 | }
|
|---|
| 490 | { local $/; open IN, $errfile; $errors = <IN>; close IN }
|
|---|
| 491 | 1 while unlink @tmpfiles;
|
|---|
| 492 | }
|
|---|
| 493 | print $output;
|
|---|
| 494 | print STDERR $errors;
|
|---|
| 495 | if ($debugging && ($errors || $? || ($output =~ /not ok/))) {
|
|---|
| 496 | my $lnum = 0;
|
|---|
| 497 | for $line (split '\n', $code) {
|
|---|
| 498 | printf "%3d: %s\n", ++$lnum, $line;
|
|---|
| 499 | }
|
|---|
| 500 | }
|
|---|
| 501 | printf "not ok: exited with error code %04X\n", $? if $?;
|
|---|
| 502 | print '#', "-" x 30, "\n" if $debugging;
|
|---|
| 503 |
|
|---|
| 504 | } # End of foreach $within
|
|---|
| 505 | } # End of foreach $where_declared
|
|---|
| 506 | } # End of foreach $inner_type
|
|---|
| 507 |
|
|---|
| 508 | }
|
|---|
| 509 |
|
|---|
| 510 | # The following dumps core with perl <= 5.8.0 (bugid 9535) ...
|
|---|
| 511 | BEGIN { $vanishing_pad = sub { eval $_[0] } }
|
|---|
| 512 | $some_var = 123;
|
|---|
| 513 | test { $vanishing_pad->( '$some_var' ) == 123 };
|
|---|
| 514 |
|
|---|
| 515 | # ... and here's another coredump variant - this time we explicitly
|
|---|
| 516 | # delete the sub rather than using a BEGIN ...
|
|---|
| 517 |
|
|---|
| 518 | sub deleteme { $a = sub { eval '$newvar' } }
|
|---|
| 519 | deleteme();
|
|---|
| 520 | *deleteme = sub {}; # delete the sub
|
|---|
| 521 | $newvar = 123; # realloc the SV of the freed CV
|
|---|
| 522 | test { $a->() == 123 };
|
|---|
| 523 |
|
|---|
| 524 | # ... and a further coredump variant - the fixup of the anon sub's
|
|---|
| 525 | # CvOUTSIDE pointer when the middle eval is freed, wasn't good enough to
|
|---|
| 526 | # survive the outer eval also being freed.
|
|---|
| 527 |
|
|---|
| 528 | $x = 123;
|
|---|
| 529 | $a = eval q(
|
|---|
| 530 | eval q[
|
|---|
| 531 | sub { eval '$x' }
|
|---|
| 532 | ]
|
|---|
| 533 | );
|
|---|
| 534 | @a = ('\1\1\1\1\1\1\1') x 100; # realloc recently-freed CVs
|
|---|
| 535 | test { $a->() == 123 };
|
|---|
| 536 |
|
|---|
| 537 | # this coredumped on <= 5.8.0 because evaling the closure caused
|
|---|
| 538 | # an SvFAKE to be added to the outer anon's pad, which was then grown.
|
|---|
| 539 | my $outer;
|
|---|
| 540 | sub {
|
|---|
| 541 | my $x;
|
|---|
| 542 | $x = eval 'sub { $outer }';
|
|---|
| 543 | $x->();
|
|---|
| 544 | $a = [ 99 ];
|
|---|
| 545 | $x->();
|
|---|
| 546 | }->();
|
|---|
| 547 | test {1};
|
|---|
| 548 |
|
|---|
| 549 | # [perl #17605] found that an empty block called in scalar context
|
|---|
| 550 | # can lead to stack corruption
|
|---|
| 551 | {
|
|---|
| 552 | my $x = "foooobar";
|
|---|
| 553 | $x =~ s/o//eg;
|
|---|
| 554 | test { $x eq 'fbar' }
|
|---|
| 555 | }
|
|---|
| 556 |
|
|---|
| 557 | # DAPM 24-Nov-02
|
|---|
| 558 | # SvFAKE lexicals should be visible thoughout a function.
|
|---|
| 559 | # On <= 5.8.0, the third test failed, eg bugid #18286
|
|---|
| 560 |
|
|---|
| 561 | {
|
|---|
| 562 | my $x = 1;
|
|---|
| 563 | sub fake {
|
|---|
| 564 | test { sub {eval'$x'}->() == 1 };
|
|---|
| 565 | { $x; test { sub {eval'$x'}->() == 1 } }
|
|---|
| 566 | test { sub {eval'$x'}->() == 1 };
|
|---|
| 567 | }
|
|---|
| 568 | }
|
|---|
| 569 | fake();
|
|---|
| 570 |
|
|---|
| 571 | # undefining a sub shouldn't alter visibility of outer lexicals
|
|---|
| 572 |
|
|---|
| 573 | {
|
|---|
| 574 | $x = 1;
|
|---|
| 575 | my $x = 2;
|
|---|
| 576 | sub tmp { sub { eval '$x' } }
|
|---|
| 577 | my $a = tmp();
|
|---|
| 578 | undef &tmp;
|
|---|
| 579 | test { $a->() == 2 };
|
|---|
| 580 | }
|
|---|
| 581 |
|
|---|
| 582 | # handy class: $x = Watch->new(\$foo,'bar')
|
|---|
| 583 | # causes 'bar' to be appended to $foo when $x is destroyed
|
|---|
| 584 | sub Watch::new { bless [ $_[1], $_[2] ], $_[0] }
|
|---|
| 585 | sub Watch::DESTROY { ${$_[0][0]} .= $_[0][1] }
|
|---|
| 586 |
|
|---|
| 587 |
|
|---|
| 588 | # bugid 1028:
|
|---|
| 589 | # nested anon subs (and associated lexicals) not freed early enough
|
|---|
| 590 |
|
|---|
| 591 | sub linger {
|
|---|
| 592 | my $x = Watch->new($_[0], '2');
|
|---|
| 593 | sub {
|
|---|
| 594 | $x;
|
|---|
| 595 | my $y;
|
|---|
| 596 | sub { $y; };
|
|---|
| 597 | };
|
|---|
| 598 | }
|
|---|
| 599 | {
|
|---|
| 600 | my $watch = '1';
|
|---|
| 601 | linger(\$watch);
|
|---|
| 602 | test { $watch eq '12' }
|
|---|
| 603 | }
|
|---|
| 604 |
|
|---|
| 605 | require "./test.pl";
|
|---|
| 606 |
|
|---|
| 607 | curr_test(182);
|
|---|
| 608 |
|
|---|
| 609 | # Because change #19637 was not applied to 5.8.1.
|
|---|
| 610 | SKIP: { skip("tests not in 5.8.", 3) }
|
|---|
| 611 |
|
|---|
| 612 | $test= 185;
|
|---|
| 613 |
|
|---|
| 614 | {
|
|---|
| 615 | # bugid #23265 - this used to coredump during destruction of PL_maincv
|
|---|
| 616 | # and its children
|
|---|
| 617 |
|
|---|
| 618 | my $progfile = "b23265.pl";
|
|---|
| 619 | open(T, ">$progfile") or die "$0: $!\n";
|
|---|
| 620 | print T << '__EOF__';
|
|---|
| 621 | print
|
|---|
| 622 | sub {$_[0]->(@_)} -> (
|
|---|
| 623 | sub {
|
|---|
| 624 | $_[1]
|
|---|
| 625 | ? $_[0]->($_[0], $_[1] - 1) . sub {"x"}->()
|
|---|
| 626 | : "y"
|
|---|
| 627 | },
|
|---|
| 628 | 2
|
|---|
| 629 | )
|
|---|
| 630 | , "\n"
|
|---|
| 631 | ;
|
|---|
| 632 | __EOF__
|
|---|
| 633 | close T;
|
|---|
| 634 | my $got = runperl(progfile => $progfile);
|
|---|
| 635 | test { chomp $got; $got eq "yxx" };
|
|---|
| 636 | END { 1 while unlink $progfile }
|
|---|
| 637 | }
|
|---|
| 638 |
|
|---|
| 639 | {
|
|---|
| 640 | # bugid #24914 = used to coredump restoring PL_comppad in the
|
|---|
| 641 | # savestack, due to the early freeing of the anon closure
|
|---|
| 642 |
|
|---|
| 643 | my $got = runperl(stderr => 1, prog =>
|
|---|
| 644 | 'sub d {die} my $f; $f = sub {my $x=1; $f = 0; d}; eval{$f->()}; print qq(ok\n)'
|
|---|
| 645 | );
|
|---|
| 646 | test { $got eq "ok\n" };
|
|---|
| 647 | }
|
|---|
| 648 |
|
|---|
| 649 | # After newsub is redefined outside the BEGIN, it's CvOUTSIDE should point
|
|---|
| 650 | # to main rather than BEGIN, and BEGIN should be freed.
|
|---|
| 651 |
|
|---|
| 652 | {
|
|---|
| 653 | my $flag = 0;
|
|---|
| 654 | sub X::DESTROY { $flag = 1 }
|
|---|
| 655 | {
|
|---|
| 656 | my $x;
|
|---|
| 657 | BEGIN {$x = \&newsub }
|
|---|
| 658 | sub newsub {};
|
|---|
| 659 | $x = bless {}, 'X';
|
|---|
| 660 | }
|
|---|
| 661 | test { $flag == 1 };
|
|---|
| 662 | }
|
|---|