This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Update Encode to CPAN version 2.78
[perl5.git] / t / op / svleak.t
index ee646d7..076f2bf 100644 (file)
@@ -5,7 +5,7 @@
 # see if the count increases.
 
 BEGIN {
-    chdir 't';
+    chdir 't' if -d 't';
     @INC = '../lib';
     require './test.pl';
 
@@ -15,7 +15,7 @@ BEGIN {
 
 use Config;
 
-plan tests => 50;
+plan tests => 129;
 
 # run some code N times. If the number of SVs at the end of loop N is
 # greater than (N-1)*delta at the end of loop 1, we've got a leak
@@ -32,12 +32,13 @@ sub leak {
     cmp_ok($sv1-$sv0, '<=', ($n-1)*$delta, @rest);
 }
 
-# Like leak, but run a string eval instead; takes into account existing
-# string eval leaks under -Dmad.  The code is used instead of the test name
+# Like leak, but run a string eval instead.
+# The code is used instead of the test name
 # if the name is absent.
 sub eleak {
     my ($n,$delta,$code,@rest) = @_;
-    leak $n, $delta + !!$Config{mad}, sub { eval $code },
+    no warnings 'deprecated'; # Silence the literal control character warning
+    leak $n, $delta, sub { eval $code },
          @rest ? @rest : $code
 }
 
@@ -69,6 +70,61 @@ leak(5, 0, sub {},                 "basic check 1 of leak test infrastructure");
 leak(5, 0, sub {push @a,1;pop @a}, "basic check 2 of leak test infrastructure");
 leak(5, 1, sub {push @a,1;},       "basic check 3 of leak test infrastructure");
 
+# delete
+{
+    my $key = "foo";
+    $key++ while exists $ENV{$key};
+    leak(2, 0, sub { delete local $ENV{$key} },
+       'delete local on nonexistent env var');
+}
+
+# Fatal warnings
+my $f = "use warnings FATAL =>";
+my $all = "$f 'all';";
+eleak(2, 0, "$f 'deprecated'; qq|\\c\{|", 'qq|\c{| with fatal warnings');
+eleak(2, 0, "$f 'syntax'; qq|\\c`|", 'qq|\c`| with fatal warnings');
+eleak(2, 0, "$all /\$\\ /", '/$\ / with fatal warnings');
+eleak(2, 0, "$all s//\\1/", 's//\1/ with fatal warnings');
+eleak(2, 0, "$all qq|\\i|", 'qq|\i| with fatal warnings');
+eleak(2, 0, "$f 'digit'; qq|\\o{9}|", 'qq|\o{9}| with fatal warnings');
+eleak(3, 1, "$f 'misc'; sub foo{} sub foo:lvalue",
+     'ignored :lvalue with fatal warnings');
+eleak(2, 0, "no warnings; use feature ':all'; $f 'misc';
+             my sub foo{} sub foo:lvalue",
+     'ignored mysub :lvalue with fatal warnings');
+eleak(2, 0, "no warnings; use feature ':all'; $all
+             my sub foo{} sub foo:lvalue{}",
+     'fatal mysub redef warning');
+eleak(2, 0, "$all sub foo{} sub foo{}", 'fatal sub redef warning');
+eleak(2, 0, "$all *x=sub {}",
+     'fatal sub redef warning with sub-to-glob assignment');
+eleak(2, 0, "$all *x=sub() {1}",
+     'fatal const sub redef warning with sub-to-glob assignment');
+eleak(2, 0, "$all XS::APItest::newCONSTSUB(\\%main::=>name=>0=>1)",
+     'newCONSTSUB sub redefinition with fatal warnings');
+eleak(2, 0, "$f 'misc'; my\$a,my\$a", 'double my with fatal warnings');
+eleak(2, 0, "$f 'misc'; our\$a,our\$a", 'double our with fatal warnings');
+eleak(2, 0, "$f 'closure';
+             sub foo { my \$x; format=\n\@\n\$x\n.\n} write; ",
+     'format closing over unavailable var with fatal warnings');
+eleak(2, 0, "$all /(?{})?/ ", '(?{})? with fatal warnings');
+eleak(2, 0, "$all /(?{})+/ ", '(?{})+ with fatal warnings');
+eleak(2, 0, "$all /[\\i]/ ", 'invalid charclass escape with fatal warns');
+eleak(2, 0, "$all /[:foo:]/ ", '/[:foo:]/ with fatal warnings');
+eleak(2, 0, "$all /[a-\\d]/ ", '[a-\d] char class with fatal warnings');
+eleak(2, 0, "$all v111111111111111111111111111111111111111111111111",
+     'vstring num overflow with fatal warnings');
+
+eleak(2, 0, 'sub{<*>}');
+# Use a random number of ops, so that the glob op does not reuse the same
+# address each time, giving us false passes.
+leak(2, 0, sub { eval '$x+'x(1 + rand() * 100) . '<*>'; },
+    'freeing partly iterated glob');
+
+eleak(2, 0, 'goto sub {}', 'goto &sub in eval');
+eleak(2, 0, '() = sort { goto sub {} } 1,2', 'goto &sub in sort');
+eleak(2, 0, '/(?{ goto sub {} })/', 'goto &sub in regexp');
+
 sub TIEARRAY   { bless [], $_[0] }
 sub FETCH      { $_[0]->[$_[1]] }
 sub STORE      { $_[0]->[$_[1]] = $_[2] }
@@ -79,6 +135,19 @@ sub STORE   { $_[0]->[$_[1]] = $_[2] }
     leak(5, 0, sub {local $a[0]}, "local \$tied[0]");
 }
 
+# Overloading
+require overload;
+eleak(2, 0, "BEGIN{overload::constant integer=>sub{}} 1,1,1,1,1,1,1,1,1,1",
+     '"too many errors" from constant overloading returning undef');
+# getting this one to leak was complicated; we have to unset LOCALIZE_HH:
+eleak(2, 0, 'BEGIN{overload::constant integer=>sub{}; $^H &= ~ 0x00020000}
+             1,1,1,1,1,1,1,1,1,1',
+     '"too many errors" from constant overloading with $^H sabotaged');
+eleak(2, 0, "BEGIN{overload::constant integer=>sub{}; undef %^H}
+             1,1,1,1,1,1,1,1,1,1",
+     '"too many errors" from constant overloading with %^H undefined');
+
+
 # [perl #74484]  repeated tries leaked SVs on the tmps stack
 
 leak_expr(5, 0, q{"YYYYYa" =~ /.+?(a(.+?)|b)/ }, "trie leak");
@@ -91,36 +160,43 @@ leak_expr(5, 0, q{"YYYYYa" =~ /.+?(a(.+?)|b)/ }, "trie leak");
     my $s;
     my @a;
     my @count = (0) x 4; # pre-allocate
-
-    grep qr/1/ && ($count[$_] = sv_count()) && 99,  0..3;
+    # Using 0..3 with constant endpoints will cause an erroneous test fail-
+    # ure, as the SV in the op tree needs to be copied (to protect it),
+    # but copying happens *during iteration*, causing the number of SVs to
+    # go up.  Using a variable (0..$_3) will cause evaluation of the range
+    # operator at run time, not compile time, so the values will already be
+    # on the stack before grep starts.
+    my $_3 = 3;
+
+    grep qr/1/ && ($count[$_] = sv_count()) && 99,  0..$_3;
     is(@count[3] - @count[0], 0, "void   grep expr:  no new tmps per iter");
-    grep { qr/1/ && ($count[$_] = sv_count()) && 99 }  0..3;
+    grep { qr/1/ && ($count[$_] = sv_count()) && 99 }  0..$_3;
     is(@count[3] - @count[0], 0, "void   grep block: no new tmps per iter");
 
-    $s = grep qr/1/ && ($count[$_] = sv_count()) && 99,  0..3;
+    $s = grep qr/1/ && ($count[$_] = sv_count()) && 99,  0..$_3;
     is(@count[3] - @count[0], 0, "scalar grep expr:  no new tmps per iter");
-    $s = grep { qr/1/ && ($count[$_] = sv_count()) && 99 }  0..3;
+    $s = grep { qr/1/ && ($count[$_] = sv_count()) && 99 }  0..$_3;
     is(@count[3] - @count[0], 0, "scalar grep block: no new tmps per iter");
 
-    @a = grep qr/1/ && ($count[$_] = sv_count()) && 99,  0..3;
+    @a = grep qr/1/ && ($count[$_] = sv_count()) && 99,  0..$_3;
     is(@count[3] - @count[0], 0, "list   grep expr:  no new tmps per iter");
-    @a = grep { qr/1/ && ($count[$_] = sv_count()) && 99 }  0..3;
+    @a = grep { qr/1/ && ($count[$_] = sv_count()) && 99 }  0..$_3;
     is(@count[3] - @count[0], 0, "list   grep block: no new tmps per iter");
 
 
-    map qr/1/ && ($count[$_] = sv_count()) && 99,  0..3;
+    map qr/1/ && ($count[$_] = sv_count()) && 99,  0..$_3;
     is(@count[3] - @count[0], 0, "void   map expr:  no new tmps per iter");
-    map { qr/1/ && ($count[$_] = sv_count()) && 99 }  0..3;
+    map { qr/1/ && ($count[$_] = sv_count()) && 99 }  0..$_3;
     is(@count[3] - @count[0], 0, "void   map block: no new tmps per iter");
 
-    $s = map qr/1/ && ($count[$_] = sv_count()) && 99,  0..3;
+    $s = map qr/1/ && ($count[$_] = sv_count()) && 99,  0..$_3;
     is(@count[3] - @count[0], 0, "scalar map expr:  no new tmps per iter");
-    $s = map { qr/1/ && ($count[$_] = sv_count()) && 99 }  0..3;
+    $s = map { qr/1/ && ($count[$_] = sv_count()) && 99 }  0..$_3;
     is(@count[3] - @count[0], 0, "scalar map block: no new tmps per iter");
 
-    @a = map qr/1/ && ($count[$_] = sv_count()) && 99,  0..3;
+    @a = map qr/1/ && ($count[$_] = sv_count()) && 99,  0..$_3;
     is(@count[3] - @count[0], 3, "list   map expr:  one new tmp per iter");
-    @a = map { qr/1/ && ($count[$_] = sv_count()) && 99 }  0..3;
+    @a = map { qr/1/ && ($count[$_] = sv_count()) && 99 }  0..$_3;
     is(@count[3] - @count[0], 3, "list   map block: one new tmp per iter");
 
 }
@@ -140,6 +216,9 @@ SKIP:
   ok(!$weak, "hash referenced weakened SV released");
 }
 
+# prototype() errors
+leak(2,0, sub { eval { prototype "CORE::fu" } }, 'prototype errors');
+
 # RT #72246: rcatline memory leak on bad $/
 
 leak(2, 0,
@@ -172,29 +251,92 @@ leak(2, 0,
 
 eleak(2,0,'/[:]/');
 eleak(2,0,'/[\xdf]/i');
+eleak(2,0,'s![^/]!!');
+eleak(2,0,'/[pp]/');
+eleak(2,0,'/[[:ascii:]]/');
+eleak(2,0,'/[[.zog.]]/');
+eleak(2,0,'/[.zog.]/');
+eleak(2,0,'/|\W/', '/|\W/ [perl #123198]');
+eleak(2,0,'no warnings; /(?[])/');
+eleak(2,0,'no warnings; /(?[[a]+[b]])/');
+eleak(2,0,'no warnings; /(?[[a]-[b]])/');
+eleak(2,0,'no warnings; /(?[[a]&[b]])/');
+eleak(2,0,'no warnings; /(?[[a]|[b]])/');
+eleak(2,0,'no warnings; /(?[[a]^[b]])/');
+eleak(2,0,'no warnings; /(?[![a]])/');
+eleak(2,0,'no warnings; /(?[\p{Word}])/');
+eleak(2,0,'no warnings; /(?[[a]+)])/');
+eleak(2,0,'no warnings; /(?[\d\d)])/');
+
+# These can generate one ref count, but just  once.
+eleak(4,1,'chr(0x100) =~ /[[:punct:]]/');
+eleak(4,1,'chr(0x100) =~ /[[:^punct:]]/');
+eleak(4,1,'chr(0x100) =~ /[[:word:]]/');
+eleak(4,1,'chr(0x100) =~ /[[:^word:]]/');
+
+eleak(2,0,'chr(0x100) =~ /\P{Assigned}/');
+leak(2,0,sub { /(??{})/ }, '/(??{})/');
 
 leak(2,0,sub { !$^V }, '[perl #109762] version object in boolean context');
 
 
 # [perl #114356] run-time rexexp with unchanging pattern got
 # inflated refcounts
-
-SKIP: {
-    skip "disabled under -Dmad (eval leaks)" if $Config{mad};
-    leak(2, 0, sub { eval q{ my $x = "x"; "abc" =~ /$x/ for 1..5 } }, '#114356');
+eleak(2, 0, q{ my $x = "x"; "abc" =~ /$x/ for 1..5 }, '#114356');
+
+eleak(2, 0, 'sub', '"sub" with nothing following');
+eleak(2, 0, '+sub:a{}', 'anon subs with invalid attributes');
+eleak(2, 0, 'no warnings; sub a{1 1}', 'sub with syntax error');
+eleak(2, 0, 'no warnings; sub {1 1}', 'anon sub with syntax error');
+eleak(2, 0, 'no warnings; use feature ":all"; my sub a{1 1}',
+     'my sub with syntax error');
+
+# Reification (or lack thereof)
+leak(2, 0, sub { sub { local $_[0]; shift }->(1) },
+    'local $_[0] on surreal @_, followed by shift');
+leak(2, 0, sub { sub { local $_[0]; \@_ }->(1) },
+    'local $_[0] on surreal @_, followed by reification');
+
+sub recredef {}
+sub Recursive::Redefinition::DESTROY {
+    *recredef = sub { CORE::state $x } # state makes it cloneable
 }
+leak(2, 0, sub {
+    bless \&recredef, "Recursive::Redefinition"; eval "sub recredef{}"
+}, 'recursive sub redefinition');
 
 # Syntax errors
 eleak(2, 0, '"${<<END}"
                  ', 'unterminated here-doc in quotes in multiline eval');
 eleak(2, 0, '"${<<END
                }"', 'unterminated here-doc in multiline quotes in eval');
-leak(2, !!$Config{mad}, sub { eval { do './op/svleak.pl' } },
+leak(2, 0, sub { eval { do './op/svleak.pl' } },
         'unterminated here-doc in file');
 eleak(2, 0, 'tr/9-0//');
 eleak(2, 0, 'tr/a-z-0//');
 eleak(2, 0, 'no warnings; nonexistent_function 33838',
         'bareword followed by number');
+eleak(2, 0, '//dd;'x20, '"too many errors" when parsing m// flags');
+eleak(2, 0, 's///dd;'x20, '"too many errors" when parsing s/// flags');
+eleak(2, 0, 'no warnings; 2 2;BEGIN{}',
+      'BEGIN block after syntax error');
+{
+    local %INC; # in case Errno is already loaded
+    eleak(2, 0, 'no warnings; 2@!{',
+                'implicit "use Errno" after syntax error');
+}
+eleak(2, 0, "\"\$\0\356\"", 'qq containing $ <null> something');
+eleak(2, 0, 'END OF TERMS AND CONDITIONS', 'END followed by words');
+eleak(2, 0, "+ + +;qq|\\N{a}|"x10,'qq"\N{a}" after errors');
+eleak(2, 0, "qq|\\N{%}|",      'qq"\N{%}" (invalid charname)');
+eleak(2, 0, "qq|\\N{au}|;",    'qq"\N{invalid}"');
+eleak(2, 0, "qq|\\c|;"x10,     '"too many errors" from qq"\c"');
+eleak(2, 0, "qq|\\o|;"x10,     '"too many errors" from qq"\o"');
+eleak(2, 0, "qq|\\x{|;"x10,    '"too many errors" from qq"\x{"');
+eleak(2, 0, "qq|\\N|;"x10,     '"too many errors" from qq"\N"');
+eleak(2, 0, "qq|\\N{|;"x10,    '"too many errors" from qq"\N{"');
+eleak(2, 0, "qq|\\N{U+GETG}|;"x10,'"too many errors" from qq"\N{U+JUNK}"');
+
 
 # [perl #114764] Attributes leak scalars
 leak(2, 0, sub { eval 'my $x : shared' }, 'my $x :shared used to leak');
@@ -227,6 +369,7 @@ leak(2, 0, sub {
     eval {%a = ($die_on_fetch, 0)}; # key
     eval {%a = (0, $die_on_fetch)}; # value
     eval {%a = ($die_on_fetch, $die_on_fetch)}; # both
+    eval {%a = ($die_on_fetch)}; # key, odd elements
 }, 'hash assignment does not leak');
 leak(2, 0, sub {
     eval {@a = ($die_on_fetch)};
@@ -243,7 +386,7 @@ package hhtie {
     sub FIRSTKEY { keys %{$_[0][0]}; each %{$_[0][0]} }
     sub NEXTKEY  { each %{$_[0][0]} }
 }
-leak(2,!!$Config{mad}, sub {
+leak(2, 0, sub {
     eval q`
        BEGIN {
            $hhtie::explosive = 0;
@@ -305,13 +448,12 @@ leak(2, 0, sub {
 # Run-time regexp code blocks
 {
     use re 'eval';
-    my $madness = !!$Config{mad};
     my @tests = ('[(?{})]','(?{})');
     for my $t (@tests) {
-       leak(2, $madness, sub {
+       leak(2, 0, sub {
            / $t/;
        }, "/ \$x/ where \$x is $t does not leak");
-       leak(2, $madness, sub {
+       leak(2, 0, sub {
            /(?{})$t/;
        }, "/(?{})\$x/ where \$x is $t does not leak");
     }
@@ -333,3 +475,21 @@ leak(2, 0, sub {
 
 
 leak(2,0,sub{eval{require untohunothu}}, 'requiring nonexistent module');
+
+# [perl #120939]
+use constant const_av_xsub_leaked => 1 .. 3;
+leak(5, 0, sub { scalar &const_av_xsub_leaked }, "const_av_sub in scalar context");
+
+# check that OP_MULTIDEREF doesn't leak when compiled and then freed
+
+eleak(2, 0, <<'EOF', 'OP_MULTIDEREF');
+no strict;
+no warnings;
+my ($x, @a, %h, $r, $k, $i);
+$x = $a[0]{foo}{$k}{$i};
+$x = $h[0]{foo}{$k}{$i};
+$x = $r->[0]{foo}{$k}{$i};
+$x = $mdr::a[0]{foo}{$mdr::k}{$mdr::i};
+$x = $mdr::h[0]{foo}{$mdr::k}{$mdr::i};
+$x = $mdr::r->[0]{foo}{$mdr::k}{$mdr::i};
+EOF