Front page | perl.perl5.porters |
Postings from February 2000
BUGS: English's imports + local on imports + local on ties
Thread Next
From:
Tom Christiansen
Date:
February 5, 2000 16:01
Subject:
BUGS: English's imports + local on imports + local on ties
Message ID:
22484.949795255@chthon
This admittedly lengthy missive describes an assorted collection
of various bugs, unpleasantries, and surprises--depending on your
point of view--that occur in the very latest _64 release. Some
of these are hardly new, but have been with us for a goodly spell.
I very much hope that these can all be explained. The many mysteries
I'm about to touch upon all began simply enough, when I noticed
that English.pm contains:
*LAST_PAREN_MATCH = *+ ;
*FORMAT_LINES_LEFT = *- ;
which leads, reasonably enough, to
$LAST_PAREN_MATCH being an alias for $+
$FORMAT_LINES_LEFT being an alias for $-
However, this also inexorably leads to the
remarkably less than reasonable
@LAST_PAREN_MATCH being an alias for @+
@FORMAT_LINES_LEFT being an alias for @-
(I see that the new variables have no English equivalent, a tiny
bug that's easy to fix. What shall we call $^U and $^V?)
Now, I first wondered whether the *X = *Y style had been done elected
over the less silly *X = \$Y mechanism because of the bug in local()
demonstrated here.
#!/usr/bin/perl -lw
# fdirs - find all directories
@ARGV = qw(.) unless @ARGV;
use File::Find ();
sub find(&@) { &File::Find::find }
*name = *File::Find::name;
find { print $name if -d } @ARGV;
If you run it that way, all is well, and you get:
% perl /tmp/findirs /var/spool
/var/spool
/var/spool/ftp
/var/spool/ftp/bin
/var/spool/ftp/etc
/var/spool/ftp/hidden
/var/spool/lock
/var/spool/lpd
/var/spool/mqueue
/var/spool/output
/var/spool/uucp
/var/spool/uucppublic
However, if you use the more intuitive, but wrong, version
#!/usr/bin/perl -lw
# fdirs - find all directories
@ARGV = qw(.) unless @ARGV;
use File::Find ();
sub find(&@) { &File::Find::find }
*name = \$File::Find::name;
find { print $name if -d } @ARGV;
you now get the wrong answer entirely:
% perl /tmp/findirs /var/spool
/var/spool
/var/spool
/var/spool
/var/spool
/var/spool
/var/spool
/var/spool
/var/spool
/var/spool
/var/spool
/var/spool
I really do wish this matter were (pick one) fixed, documented,
and/or addressed. The local($name) within File::Find severs the
connection with the aliased version back up top. However, a full
typeglob aliasing does not suffer this infelicity. Am I the only
one who knows about this issue? I brought it up in early 1996, but
there it remains. There's this entry (that I believe I put) in
perltrap about this:
o If you localize an exported variable in a module, its
exported value will not change. The local name becomes
an alias to a new value but the external name is still
an alias for the original.
This, by the way, is why File::Find no longer exports its $name
variable: because the Exporter doesn't know how (didn't know how?)
to do typeglobs properly. So people are forced to use fully qualified
names for the package globals, unless they do the import on their
own as I did above.
So, is this the underlying problem with English? I really don't
think so, although it's still clearly an issue with File::Find.
I don't think so. Forgive me, but the demo below is slightly
longish.
$|=1;
print "First, let's try with the whole glob.\n\n";
{ package Test1;
*PROGRAM_NAME = *0;
print "Hello, my name is $PROGRAM_NAME\n";
system "ps $$";
{
local $PROGRAM_NAME = "a figment";
print "Hush, I'm pretending to be $PROGRAM_NAME\n";
system "ps $$";
}
}
print "End of first local. Who am I now?\n";
system "ps $$";
print "\nOk, let's try with the scalar reference.\n\n";
{
package Test2;
*PROGRAM_NAME = \$0;
print "Hello, my name is $PROGRAM_NAME\n";
system "ps $$";
{
local $PROGRAM_NAME = "a figment";
print "Hush, I'm pretending to be $PROGRAM_NAME\n";
system "ps $$";
}
}
print "End of second local. Who am I now?\n";
system "ps $$";
One would think that the second case would no longer have
the magical effect. Yet, it does:
% perl /tmp/namespoof
First, let's try with the whole glob.
Hello, my name is /tmp/namespoof
PID TT STAT TIME COMMAND
10691 pe S+ 0:00.02 perl /tmp/namespoof
Hush, I'm pretending to be a figment
PID TT STAT TIME COMMAND
10691 pe S+ 0:00.02 a figment (perl)
End of first local. Who am I now?
PID TT STAT TIME COMMAND
10691 pe S+ 0:00.02 /tmp/namespoof (perl)
Ok, let's try with the scalar reference.
Hello, my name is /tmp/namespoof
PID TT STAT TIME COMMAND
10691 pe S+ 0:00.03 /tmp/namespoof (perl)
Hush, I'm pretending to be a figment
PID TT STAT TIME COMMAND
10691 pe S+ 0:00.03 a figment (perl)
End of second local. Who am I now?
PID TT STAT TIME COMMAND
10691 pe S+ 0:00.03 /tmp/namespoof (perl)
So, local()izing an symbol imported with \$ instead
of * works ok in this case, in that the magic remains.
What's really going on here? Is magic different?
While we're on the local()ized magic issue, we have
a perl source code incompatibility not pointed out in
perldelta.
Under 5.5.054, the code attached later on produces this,
as PCB:13 explains and expects:
Testing Assignment: detected
Testing Reading: detected
Testing Matching: detected
Testing Chop: detected
Testing Filetest: detected
Testing Nesting: detected
However, when run under 5.5.640, the same code now anomalously
produces an unexpected failure:
Testing Assignment: detected
Testing Reading: detected
Testing Matching: detected
Testing Chop: detected
Testing Filetest: detected
Testing Nesting: 123missed!
Here's the once-happy module that is being hurt by this:
###################################
# put this in UnderScore.pm
###################################
package UnderScore;
use Carp;
sub TIESCALAR {
my $class = shift;
my $dummy;
return bless \$dummy => $class;
}
sub FETCH { croak "Read access to \$_ forbidden" }
sub STORE { croak "Write access to \$_ forbidden" }
sub unimport { tie($_, __PACKAGE__) }
sub import { untie $_ }
tie($_, __PACKAGE__) unless tied $_;
1;
And here's the demo code:
#!/usr/bin/perl
# nounder_demo - show how to ban $_ from your program
no UnderScore;
@tests = (
"Assignment" => sub { $_ = "Bad" },
"Reading" => sub { print },
"Matching" => sub { $x = /badness/ },
"Chop" => sub { chop },
"Filetest" => sub { -x },
"Nesting" => sub { for (1..3) { print } },
);
while ( ($name, $code) = splice(@tests, 0, 2) ) {
print "Testing $name: ";
eval { &$code };
print $@ ? "detected" : "missed!";
print "\n";
}
It seems that the magic tiedness has suddenly become lost to the
implicit local(), where not so very long ago, versionwise, it was
perfectly happy. Could someone, for any arbitrary value of "someone"
that's almost certainly either "Larry" or "Sarathy", kindly explain
to this memory-challenged soul just why this breakage was done?
There's nothing in perldelta about the change, nor is there anything
I could see in perltie about it, either. (Another bug: perltie
mention Tie::Array and Tie::Hash, but not Tie::Handle nor Tie::Scalar.)
A quick glance through Changes yielded nothing interesting, but
that doesn't mean there's nothing there, as it's unclear what
triggered this breakage. We could use a test suite for it, too.
I offer up the code previous for this very purpose.
--tom, who will be completely astounded if anyone actually reads
and responds to this, given the last few bug reports with
accompanying proposals which I've filed.
Thread Next
-
BUGS: English's imports + local on imports + local on ties
by Tom Christiansen