develooper Front page | perl.perl5.porters | Postings from February 2000

handle autoviv bug + proposed fix

From:
Tom Christiansen
Date:
February 26, 2000 05:41
Subject:
handle autoviv bug + proposed fix
Message ID:
11141.951572487@chthon
Autovivified filehandles do not enjoy the same privileges as real
handles do.  The problem is that they get autovivified to glob refs,
not to globs.  This is good for subsequent blessing, but it means
there's a niggling inconsistency: the method call dispatch logic
does not consider those two interchangeable, but should.  Watch.

Code that does this is fine:

    $ob = 'STDOUT';
    $ob->method();

or this:

    $ob = *STDOUT;
    $ob->method();

or this:

    $ob = *STDOUT{IO};
    $ob->method();

but not this one:

    $ob = \*STDOUT;
    $ob->method();

What this ends up meaning is that if you let your handle autovivify,
you can't do the things on it you can do on real ones.  For example:

    use IO::Handle qw/:DEFAULT autoflush/;
    open(my $fh, "> /dev/null") 
        or die "can't open /dev/null: $!";
    for $handle ($fh, *$fh{IO}) {
        printf "Calling autoflush function on %12s ", $handle;
        eval { autoflush($handle) };
        print $@ ? $@ : "successfully.\n";
        printf "Calling autoflush method on   %12s ", $handle;
        eval { $handle->autoflush() };
        print $@ ? $@ : "successfully.\n";
        print "\n";
    } 

When run, that produces this output:

    Calling autoflush function on GLOB(0xa14b4) successfully.
    Calling autoflush method on   GLOB(0xa14b4) Can't call method "autoflush" on unblessed reference at /tmp/blesstest line 44.

    Calling autoflush function on IO::Handle=IO(0xa14a8) successfully.
    Calling autoflush method on   IO::Handle=IO(0xa14a8) successfully.

To demonstrate the issue without autovivification, observe that this:

    use IO::Handle qw/:DEFAULT autoflush/;
    @styles = qw[ STDOUT *STDOUT \*STDOUT *STDOUT{IO} ];
    for $name (@styles) {
        printf "Calling autoflush function on %12s ", $name;
        $thingie = eval $name; die if $@;
        eval { autoflush($thingie) };
        print $@ ? $@ : "successfully.\n";
        printf "Calling autoflush method on   %12s ", $name;
        $thingie = eval $name; die if $@;
        eval { $thingie->autoflush() };
        print $@ ? $@ : "successfully.\n";
        print "\n";
    } 

Produces this output:

    Calling autoflush function on       STDOUT successfully.
    Calling autoflush method on         STDOUT successfully.

    Calling autoflush function on      *STDOUT successfully.
    Calling autoflush method on        *STDOUT successfully.

    Calling autoflush function on     \*STDOUT successfully.
    Calling autoflush method on       \*STDOUT Can't call method "autoflush" on unblessed reference at /tmp/blesstest line 21.

    Calling autoflush function on  *STDOUT{IO} successfully.
    Calling autoflush method on    *STDOUT{IO} successfully.

What should happen, though, is for to pretend that a ref(GLOB) is
a GLOB, just as we do in the rest of the places this occurs, such
as all these valid cases:

    print {   STDOUT     }  "nice output";
    print {  *STDOUT     }  "nice output";
    print { \*STDOUT     }  "nice output";
    print {  *STDOUT{IO} }  "nice output";

This all stems from code around line number 2731 of pp_hot.c:

    if (SvROK(sv))
        ob = (SV*)SvRV(sv);
    else {
        GV* iogv;

        packname = Nullch;
        if (!SvOK(sv) ||
            !(packname = SvPV(sv, packlen)) ||
            !(iogv = gv_fetchpv(packname, FALSE, SVt_PVIO)) ||
            !(ob=(SV*)GvIO(iogv)))
        {
            if (!packname ||
                ((*(U8*)packname >= 0xc0 && DO_UTF8(sv))
                    ? !isIDFIRST_utf8((U8*)packname)
                    : !isIDFIRST(*packname)
                ))
            {
                Perl_croak(aTHX_ "Can't call method \"%s\" %s", name,
                           SvOK(sv) ? "without a package or object reference"
                                    : "on an undefined value");
            }
            stash = gv_stashpvn(packname, packlen, TRUE);
            goto fetch;
        }
        *(PL_stack_base + TOPMARK + 1) = sv_2mortal(newRV((SV*)iogv));
    }

    if (!ob || !SvOBJECT(ob))
        Perl_croak(aTHX_ "Can't call method \"%s\" on unblessed reference",
                   name);

I imagine that to fix this problem, once you've determined that
you've got a reference, you should do more than that first if{}
(or, alternately, the last one) is doing right now.  I believe that
you should check whether you've got a non-object reference that's
actually referring to a GLOB, and if so, use some of the logic from
the else{} case to fix this up accordingly.

What do you think?

--tom



nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About