Don't recurse forever if both new() and TIESCALAR() are missing.
Abigail [Sat, 20 Feb 2010 17:55:06 +0000 (18:55 +0100)]
This should fix issue #72878. Before calling $pkg -> new in TIESCALAR,
we check whether $pkg -> new is actually the new defined in the Tie::Scalar
package; if true, croak instead of calling it.

lib/Tie/Scalar.pm
lib/Tie/Scalar.t

index 8048569..329770a 100644 (file)
@@ -104,9 +104,20 @@ sub new {
 
 sub TIESCALAR {
     my $pkg = shift;
-       if ($pkg->can('new') and $pkg ne __PACKAGE__) {
-       warnings::warnif("WARNING: calling ${pkg}->new since ${pkg}->TIESCALAR is missing");
-       $pkg->new(@_);
+    my $pkg_new = $pkg -> can ('new');
+
+    if ($pkg_new and $pkg ne __PACKAGE__) {
+        my $my_new = __PACKAGE__ -> can ('new');
+        if ($pkg_new == $my_new) {  
+            #
+            # Prevent recursion
+            #
+            croak "$pkg must define either a TIESCALAR() or a new() method";
+        }
+
+       warnings::warnif ("WARNING: calling ${pkg}->new since " .
+                          "${pkg}->TIESCALAR is missing");
+       $pkg -> new (@_);
     }
     else {
        croak "$pkg doesn't define a TIESCALAR method";
index 3c5d9b6..fb33ca1 100644 (file)
@@ -17,7 +17,7 @@ sub new { 'Fooled you.' }
 package main;
 
 use vars qw( $flag );
-use Test::More tests => 13;
+use Test::More tests => 16;
 
 use_ok( 'Tie::Scalar' );
 
@@ -74,3 +74,37 @@ sub new {
 sub DESTROY {
        $main::flag = 1;
 }
+
+
+#
+# Bug #72878: don't recurse forever if both new and TIESCALAR are missing.
+#
+package main;
+
+@NoMethods::ISA = qw [Tie::Scalar];
+
+eval {tie my $foo => "NoMethods"};
+
+like $@ =>
+    qr /\QNoMethods must define either a TIESCALAR() or a new() method/,
+    "croaks if both new() and TIESCALAR() are missing";
+
+#
+# Don't croak on missing new/TIESCALAR if you're inheriting one.
+#
+my $called1 = 0;
+my $called2 = 0;
+
+sub HasMethod1::new {$called1 ++}
+   @HasMethod1::ISA        = qw [Tie::Scalar];
+   @InheritHasMethod1::ISA = qw [HasMethod1];
+
+sub HasMethod2::TIESCALAR {$called2 ++}
+   @HasMethod2::ISA        = qw [Tie::Scalar];
+   @InheritHasMethod2::ISA = qw [HasMethod2];
+
+my $r1 = eval {tie my $foo => "InheritHasMethod1"; 1};
+my $r2 = eval {tie my $foo => "InheritHasMethod2"; 1};
+
+ok $r1 && $called1, "inheriting new() does not croak";
+ok $r2 && $called2, "inheriting TIESCALAR() does not croak";