xref: /openbsd/gnu/usr.bin/perl/t/comp/retainedlines.t (revision 9e6efb0a)
1#!./perl -w
2
3# Check that lines from eval are correctly retained by the debugger
4
5# Uncomment this for testing, but don't leave it in for "production", as
6# we've not yet verified that use works.
7# use strict;
8
9print "1..109\n";
10my $test = 0;
11
12sub failed {
13    my ($got, $expected, $name) = @_;
14
15    print "not ok $test - $name\n";
16    my @caller = caller(1);
17    print "# Failed test at $caller[1] line $caller[2]\n";
18    if (defined $got) {
19	print "# Got '$got'\n";
20    } else {
21	print "# Got undef\n";
22    }
23    print "# Expected $expected\n";
24    return;
25}
26
27sub is($$$) {
28    my ($got, $expect, $name) = @_;
29    $test = $test + 1;
30    if (defined $expect) {
31	if (defined $got && $got eq $expect) {
32	    print "ok $test - $name\n";
33	    return 1;
34	}
35	failed($got, "'$expect'", $name);
36    } else {
37	if (!defined $got) {
38	    print "ok $test - $name\n";
39	    return 1;
40	}
41	failed($got, 'undef', $name);
42    }
43}
44
45$^P = 0xA;
46
47my @before = grep { /eval/ } keys %::;
48
49is ((scalar @before), 0, "No evals");
50
51my %seen;
52
53sub check_retained_lines {
54    my ($prog, $name) = @_;
55    # Is there a more efficient way to write this?
56    my @expect_lines = (undef, map ({"$_\n"} split "\n", $prog), "\n", ';');
57
58    # sort in decreasing number so that $keys[0] is the from the most
59    # recent eval. In theory we should only have one, but if something
60    # breaks we might have more than one, and keys will return them in a
61    # random order, so if we dont do this then failing tests will have
62    # inconsistent results from run to run.
63    my @keys = map { $_->[0] }
64               sort { $b->[1] <=> $a->[1] }
65               map { (!$seen{$_} and /eval (\d+)/) ? [ $_, $1 ] : ()  }
66               keys %::;
67
68    is ((scalar @keys), 1, "1 new eval");
69
70    my @got_lines = @{$::{$keys[0]}};
71
72    is ((scalar @got_lines),
73	(scalar @expect_lines), "Right number of lines for $name");
74
75    for (0..$#expect_lines) {
76	is ($got_lines[$_], $expect_lines[$_], "Line $_ is correct");
77    }
78    # if we are "leaking" evals we only want to fail the current test,
79    # so we need to mark them all seen (older code only marked $keys[0]
80    # seen and this caused tests to fail that actually worked properly.)
81    $seen{$_}++ for @keys;
82}
83
84my $name = 'foo';
85
86for my $sep (' ', "\0") {
87
88    my $prog = "sub $name {
89    'Perl${sep}Rules'
90};
911;
92";
93
94    eval $prog or die;
95    check_retained_lines($prog, ord $sep);
96    $name++;
97}
98
99{
100  # This contains a syntax error
101  my $prog = "sub $name {
102    'This is $name'
103  }
104# 10 errors to triger a croak during compilation.
1051 +; 1 +; 1 +; 1 +; 1 +;
1061 +; 1 +; 1 +; 1 +; 1 +;
1071 +; # and one more for good measure.
108";
109
110  eval $prog and die;
111
112  is (eval "$name()", "This is $name", "Subroutine was compiled, despite error")
113    or print STDERR "# $@\n";
114
115  check_retained_lines($prog,
116		       'eval that defines subroutine but has syntax error');
117  $name++;
118}
119
120foreach my $flags (0x0, 0x800, 0x1000, 0x1800) {
121    local $^P = $^P | $flags;
122    # This is easier if we accept that the guts eval will add a trailing \n
123    # for us
124    my $prog = "1 + 1 + 1\n";
125    my $fail = "1 +;\n" x 11; # we need 10 errors to trigger a croak during
126                              # compile, we add an extra one just for good
127                              # measure.
128
129    is (eval $prog, 3, 'String eval works');
130    if ($flags & 0x800) {
131	check_retained_lines($prog, sprintf "%#X", $^P);
132    } else {
133	my @after = grep { /eval/ } keys %::;
134
135	is (scalar @after, 0 + keys %seen,
136	    "evals that don't define subroutines are correctly cleaned up");
137    }
138
139    is (eval $fail, undef, 'Failed string eval fails');
140
141    if ($flags & 0x1000) {
142	check_retained_lines($fail, sprintf "%#X", $^P);
143    } else {
144	my @after = grep { /eval/ } keys %::;
145
146	is (scalar @after, 0 + keys %seen,
147	    "evals that fail are correctly cleaned up");
148    }
149}
150
151# BEGIN blocks that die
152for (0xA, 0) {
153  local $^P = $_;
154
155  eval (my $prog = "BEGIN{die}\n");
156
157  if ($_) {
158    check_retained_lines($prog, 'eval that defines BEGIN that dies');
159  }
160  else {
161    my @after = grep { /eval/ } keys %::;
162
163    is (scalar @after, 0 + keys %seen,
164       "evals with BEGIN{die} are correctly cleaned up");
165  }
166}
167
168for (0xA, 0) {
169  local $^P = $_;
170
171  eval (my $prog = "UNITCHECK{die}\n");
172  is (!!$@, 1, "Is \$@ true?");
173  is ($@=~/UNITCHECK failed--call queue aborted/, 1,
174      "Error is expected value?");
175
176  if ($_) {
177    check_retained_lines($prog, 'eval that defines UNITCHECK that dies');
178  }
179  else {
180    my @after = grep { /eval/ } keys %::;
181
182    is (scalar @after, 0 + keys %seen,
183       "evals with UNITCHECK{die} are correctly cleaned up");
184  }
185}
186
187
188# [perl #79442] A #line "foo" directive in a string eval was not updating
189# *{"_<foo"} in threaded perls, and was not putting the right lines into
190# the right elements of @{"_<foo"} in non-threaded perls.
191{
192  local $^P = 0x400|0x100|0x10;
193  eval qq{#line 42 "hash-line-eval"\n labadalabada()\n};
194  is $::{"_<hash-line-eval"}[42], " labadalabada()\n",
195   '#line 42 "foo" in a string eval updates @{"_<foo"}';
196  eval qq{#line 42 "figgle"\n#line 85 "doggo"\n labadalabada()\n};
197  is $::{"_<doggo"}[85], " labadalabada()\n",
198   'subsequent #line 42 "foo" in a string eval updates @{"_<foo"}';
199}
200
201# Modifying ${"_<foo"} should not stop lines from being retained.
202{
203  local $^P = 0x400|0x100|0x10;
204  eval <<'end';
205#line 42 "copfilesv-modification"
206    BEGIN{ ${"_<copfilesv-modification"} = \1 }
207#line 52 "copfilesv-modified"
208    abcdefg();
209end
210  is $::{"_<copfilesv-modified"}[52], "    abcdefg();\n",
211   '#line 42 "foo" in a str eval is not confused by ${"_<foo"} changing';
212}
213