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