This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
t/op/local.t: tests for RT #7615
[perl5.git] / t / op / smartmatch.t
index a7a33f7..ca019fd 100644 (file)
@@ -1,13 +1,16 @@
 #!./perl
 
 BEGIN {
-    chdir 't';
-    @INC = '../lib';
+    chdir 't' if -d 't';
     require './test.pl';
+    set_up_inc('../lib');
 }
 use strict;
 use warnings;
 no warnings 'uninitialized';
+no warnings 'experimental::smartmatch';
+
+++$|;
 
 use Tie::Array;
 use Tie::Hash;
@@ -17,8 +20,8 @@ my @empty;
 my %empty;
 my @sparse; $sparse[2] = 2;
 
-my $deep1 = []; push @$deep1, \$deep1;
-my $deep2 = []; push @$deep2, \$deep2;
+my $deep1 = []; push @$deep1, $deep1;
+my $deep2 = []; push @$deep2, $deep2;
 
 my @nums = (1..10);
 tie my @tied_nums, 'Tie::StdArray';
@@ -34,15 +37,38 @@ tie my %tied_hash, 'Tie::StdHash';
 }
 
 {
+    package Test::Object::StringOverload;
+    use overload '""' => sub { "object" }, fallback => 1;
+    sub new { bless { key => 1 } }
+}
+
+{
     package Test::Object::WithOverload;
-    sub new { bless { key => 'magic' } }
-    use overload '~~' => sub { my %hash = %{ $_[0] }; $_[1] eq $hash{key} };
+    sub new { bless { key => ($_[1] // 'magic') } }
+    use overload '~~' => sub {
+       my %hash = %{ $_[0] };
+       if ($_[2]) { # arguments reversed ?
+           return $_[1] eq reverse $hash{key};
+       }
+       else {
+           return $_[1] eq $hash{key};
+       }
+    };
     use overload '""' => sub { "stringified" };
     use overload 'eq' => sub {"$_[0]" eq "$_[1]"};
 }
 
 our $ov_obj = Test::Object::WithOverload->new;
+our $ov_obj_2 = Test::Object::WithOverload->new("object");
 our $obj = Test::Object::NoOverload->new;
+our $str_obj = Test::Object::StringOverload->new;
+
+my %refh;
+unless (is_miniperl()) {
+    require Tie::RefHash;
+    tie %refh, 'Tie::RefHash';
+    $refh{$ov_obj} = 1;
+}
 
 my @keyandmore = qw(key and more);
 my @fooormore = qw(foo or more);
@@ -50,9 +76,10 @@ my %keyandmore = map { $_ => 0 } @keyandmore;
 my %fooormore = map { $_ => 0 } @fooormore;
 
 # Load and run the tests
-plan "no_plan";
+plan tests => 349+2;
 
 while (<DATA>) {
+  SKIP: {
     next if /^#/ || !/\S/;
     chomp;
     my ($yn, $left, $right, $note) = split /\t+/;
@@ -69,6 +96,8 @@ while (<DATA>) {
        $res = eval "no warnings; $tstr";
     }
     else {
+       skip_if_miniperl("Doesn't work with miniperl", $yn =~ /=/ ? 2 : 1)
+           if $note =~ /MINISKIP/;
        $res = eval $tstr;
     }
 
@@ -91,6 +120,7 @@ while (<DATA>) {
        $tstr = "$right ~~ $left";
        goto test_again;
     }
+  }
 }
 
 sub foo {}
@@ -103,6 +133,55 @@ sub FALSE() { 0 }
 sub TRUE() { 1 }
 sub NOT_DEF() { undef }
 
+{
+  # [perl #123860]
+  # this can but might not crash
+  # This can but might not crash
+  #
+  # The second smartmatch would leave a &PL_sv_no on the stack for
+  # each key it checked in %!, this could then cause various types of
+  # crash or assertion failure.
+  #
+  # This isn't guaranteed to crash, but if the stack issue is
+  # re-introduced it will probably crash in one of the many smoke
+  # builds.
+  fresh_perl_is('print (q(x) ~~ q(x)) | (/x/ ~~ %!)', "1",
+               { switches => [ "-MErrno", "-M-warnings=experimental::smartmatch" ] },
+                "don't fill the stack with rubbish");
+}
+
+{
+    # [perl #123860] continued;
+    # smartmatch was failing to SPAGAIN after pushing an SV and calling
+    # pp_match, which may have resulted in the stack being realloced
+    # in the meantime. Test this by filling the stack with pregressively
+    # larger amounts of data. At some point the stack will get realloced.
+    my @a = qw(x);
+    my %h = qw(x 1);
+    my @args;
+    my $x = 1;
+    my $bad = -1;
+    for (1..1000)  {
+        push @args, $_;
+        my $exp_n  = join '-',  (@args, $x == 0);
+        my $exp_y  = join '-',  (@args, $x == 1);
+
+        my $got_an = join '-',  (@args, (/X/ ~~ @a));
+        my $got_ay = join '-',  (@args, (/x/ ~~ @a));
+        my $got_hn = join '-',  (@args, (/X/ ~~ %h));
+        my $got_hy = join '-',  (@args, (/x/ ~~ %h));
+
+        if (   $exp_n ne $got_an || $exp_n ne $got_hn
+            || $exp_y ne $got_ay || $exp_y ne $got_hy
+        ) {
+            $bad = $_;
+            last;
+        }
+    }
+    is($bad, -1, "RT 123860: stack realloc");
+}
+
+
 # Prefix character :
 #   - expected to match
 # ! - expected to not match
@@ -154,15 +233,19 @@ __DATA__
 
 # Any ~~ object overloaded
 !      \&fatal         $ov_obj
-       'magic'         $ov_obj
-!      'not magic'     $ov_obj
+       'cigam'         $ov_obj
+!      'cigam on'      $ov_obj
+!      ['cigam']       $ov_obj
+!      ['stringified'] $ov_obj
+!      { cigam => 1 }  $ov_obj
+!      { stringified => 1 }    $ov_obj
 !      $obj            $ov_obj
 !      undef           $ov_obj
 
 # regular object
 @      $obj            $obj
 @      $ov_obj         $obj
-     \&fatal         $obj
+=@     \&fatal         $obj
 @      \&FALSE         $obj
 @      \&foo           $obj
 @      sub { 1 }       $obj
@@ -176,10 +259,32 @@ __DATA__
 @      "key"           $obj
 @      FALSE           $obj
 
+# regular object with "" overload
+@      $obj            $str_obj
+=@     \&fatal         $str_obj
+@      \&FALSE         $str_obj
+@      \&foo           $str_obj
+@      sub { 1 }       $str_obj
+@      sub { 0 }       $str_obj
+@      %keyandmore     $str_obj
+@      {"object" => 1} $str_obj
+@      @fooormore      $str_obj
+@      ["object" => 1] $str_obj
+@      /object/        $str_obj
+@      qr/object/      $str_obj
+@      "object"        $str_obj
+@      FALSE           $str_obj
+# Those will treat the $str_obj as a string because of fallback:
+
 # object (overloaded or not) ~~ Any
        $obj            qr/NoOverload/
        $ov_obj         qr/^stringified$/
-       $ov_obj         "stringified"
+=      "$ov_obj"       "stringified"
+=      "$str_obj"      "object"
+!=     $ov_obj         "stringified"
+       $str_obj        "object"
+       $ov_obj         'magic'
+!      $ov_obj         'not magic'
 
 # ~~ Coderef
        sub{0}          sub { ref $_[0] eq "CODE" }
@@ -234,6 +339,9 @@ __DATA__
        +{}             \&fatal
        @empty          \&fatal
        %empty          \&fatal
+# sub is not special on the left
+       sub {0}         qr/^CODE/
+       sub {0}         sub { ref shift eq "CODE" }
 
 # HASH ref against:
 #   - another hash ref
@@ -251,6 +359,11 @@ __DATA__
 =      %hash           %tied_hash
        %tied_hash      %tied_hash
 !=     {"a"=>"b"}      %tied_hash
+       $ov_obj         %refh           MINISKIP
+!      "$ov_obj"       %refh           MINISKIP
+       [$ov_obj]       %refh           MINISKIP
+!      ["$ov_obj"]     %refh           MINISKIP
+       %refh           %refh           MINISKIP
 
 #  - an array ref
 #  (since this is symmetrical, tests as well hash~~array)
@@ -315,7 +428,7 @@ __DATA__
        ["foo", "bar"]          [["foo"], ["bar"]]
 !      ["foo", "bar"]          [qr/o/, "foo"]
        ["foo", undef, "bar"]   [qr/o/, undef, "bar"]
-       ["foo", undef, "bar"]   [qr/o/, "",    "bar"]
+!      ["foo", undef, "bar"]   [qr/o/, "",    "bar"]
 !      ["foo", "", "bar"]      [qr/o/, undef, "bar"]
        $deep1                  $deep1
        @$deep1                 @$deep1
@@ -353,6 +466,11 @@ __DATA__
 !      undef           [1, 2, [undef], 4]
 !      undef           @fooormore
        undef           @sparse
+       undef           [undef]
+!      0               [undef]
+!      ""              [undef]
+!      undef           [0]
+!      undef           [""]
 
 # - nested arrays and ~~ distributivity
        11              [[11]]
@@ -366,7 +484,8 @@ __DATA__
 !      2               3
        0               FALSE
        3-2             TRUE
-       undef           0
+!      undef           0
+!      (my $u)         0
 
 # Number against string
 =      2               "2"
@@ -374,6 +493,8 @@ __DATA__
 !      2               "2bananas"
 !=     2_3             "2_3"           NOWARNINGS
        FALSE           "0"
+!      undef           "0"
+!      undef           ""
 
 # Regex against string
        "x"             qr/x/
@@ -411,3 +532,42 @@ __DATA__
        @nums           {  1, '',  2, '' }
        @nums           {  1, '', 12, '' }
 !      @nums           { 11, '', 12, '' }
+
+# array slices
+       @nums[0..-1]    []
+       @nums[0..0]     [1]
+!      @nums[0..1]     [0..2]
+       @nums[0..4]     [1..5]
+
+!      undef           @nums[0..-1]
+       1               @nums[0..0]
+       2               @nums[0..1]
+!      @nums[0..1]     2
+
+       @nums[0..1]     @nums[0..1]
+
+# hash slices
+       @keyandmore{qw(not)}            [undef]
+       @keyandmore{qw(key)}            [0]
+
+       undef                           @keyandmore{qw(not)}
+       0                               @keyandmore{qw(key and more)}
+!      2                               @keyandmore{qw(key and)}
+
+       @fooormore{qw(foo)}             @keyandmore{qw(key)}
+       @fooormore{qw(foo or more)}     @keyandmore{qw(key and more)}
+
+# UNDEF
+!      3               undef
+!      1               undef
+!      []              undef
+!      {}              undef
+!      \%::main        undef
+!      [1,2]           undef
+!      %hash           undef
+!      @nums           undef
+!      "foo"           undef
+!      ""              undef
+!      !1              undef
+!      \&foo           undef
+!      sub { }         undef