Filehandle method calls load IO::File on demand
authorDavid Golden <dagolden@cpan.org>
Mon, 29 Nov 2010 04:12:12 +0000 (23:12 -0500)
committerDavid Golden <dagolden@cpan.org>
Mon, 29 Nov 2010 20:30:50 +0000 (15:30 -0500)
When a method call on a filehandle would die because the method can not
be resolved and L<IO::File> has not been loaded, Perl now loads IO::File
via C<require> and attempts method resolution again:

  open my $fh, ">", $file;
  $fh->binmode(":raw");     # loads IO::File and succeeds

This also works for globs like STDOUT, STDERR and STDIN:

  STDOUT->autoflush(1);

Because this on-demand load only happens if method resolution fails, the
legacy approach of manually loading an IO::File parent class for partial
method support still works as expected:

  use IO::Handle;
  open my $fh, ">", $file;
  $fh->autoflush(1);        # IO::File not loaded

MANIFEST
gv.c
pod/perldelta.pod
t/io/iofile.t [new file with mode: 0644]
t/io/open.t

index 5ddf5cc..d9281f4 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -4485,6 +4485,7 @@ t/io/errno.t                      See if $! is correctly set
 t/io/fflush.t                  See if auto-flush on fork/exec/system/qx works
 t/io/fs.t                      See if directory manipulations work
 t/io/inplace.t                 See if inplace editing works
+t/io/iofile.t                  See if we can load IO::File on demand
 t/io/iprefix.t                 See if inplace editing works with prefixes
 t/io/layers.t                  See if PerlIO layers work
 t/io/nargv.t                   See if nested ARGV stuff works
diff --git a/gv.c b/gv.c
index 5fd385b..4775bcc 100644 (file)
--- a/gv.c
+++ b/gv.c
@@ -718,6 +718,19 @@ Perl_gv_fetchmethod_flags(pTHX_ HV *stash, const char *name, U32 flags)
            /* Right now this is exclusively for the benefit of S_method_common
               in pp_hot.c  */
            if (stash) {
+               /* If we can't find an IO::File method, it might be a call on
+                * a filehandle. If IO:File has not been loaded, try to
+                * require it first instead of croaking */
+               const char *stash_name = HvNAME_get(stash);
+               const char *io_file = "IO/File.pm";
+               if (stash_name && strEQ(stash_name,"IO::File")
+                   && ! hv_exists(GvHVn(PL_incgv),io_file, strlen(io_file))
+               ) {
+                   require_pv(io_file);
+                   gv = gv_fetchmeth(stash, name, nend - name, 0);
+                   if (gv)
+                       return gv;
+               }
                Perl_croak(aTHX_
                           "Can't locate object method \"%s\" via package \"%.*s\"",
                           name, (int)HvNAMELEN_get(stash), HvNAME_get(stash));
index b9c5cb0..8c660f7 100644 (file)
@@ -58,6 +58,27 @@ calls C<< Devel::foo->unimport('bar') >> if the method exists.
 This is particularly useful to suppresses the default actions of a
 C<Devel::*> module's C<import> method whilst still loading it for debugging.
 
+=head2 Filehandle method calls load IO::File on demand
+
+When a method call on a filehandle would die because the method can not
+be resolved and L<IO::File> has not been loaded, Perl now loads IO::File
+via C<require> and attempts method resolution again:
+
+  open my $fh, ">", $file;
+  $fh->binmode(":raw");     # loads IO::File and succeeds
+
+This also works for globs like STDOUT, STDERR and STDIN:
+
+  STDOUT->autoflush(1);
+
+Because this on-demand load only happens if method resolution fails, the
+legacy approach of manually loading an IO::File parent class for partial
+method support still works as expected:
+
+  use IO::Handle;
+  open my $fh, ">", $file;
+  $fh->autoflush(1);        # IO::File not loaded
+
 =head1 Security
 
 XXX Any security-related notices go here.  In particular, any security
diff --git a/t/io/iofile.t b/t/io/iofile.t
new file mode 100644 (file)
index 0000000..9a5b278
--- /dev/null
@@ -0,0 +1,25 @@
+#!./perl
+
+BEGIN {
+    chdir 't' if -d 't';
+    @INC = '../lib';
+    require './test.pl';
+}
+
+$|  = 1;
+use warnings;
+use Config;
+
+plan tests => 3;
+
+# this is essentially the same as a test on a lexical filehandle in
+# t/io/open.t, but done in a separate test process against a standard
+# filehandle
+
+# check that we can call methods on filehandles auto-magically
+# and have IO::File loaded for us
+{
+    is( $INC{'IO/File.pm'}, undef, "IO::File not loaded" );
+    ok( eval { STDOUT->autoflush(1); 1 }, 'STDOUT->autoflush(1) lives' );
+    ok( $INC{'IO/File.pm'}, "IO::File now loaded" );
+}
index 5bbcb0b..5a8f008 100644 (file)
@@ -10,7 +10,7 @@ $|  = 1;
 use warnings;
 use Config;
 
-plan tests => 111;
+plan tests => 114;
 
 my $Perl = which_perl();
 
@@ -347,3 +347,13 @@ fresh_perl_is(
         # when this fails, it leaves an extra file:
         or unlink \*STDOUT;
 }
+
+# check that we can call methods on filehandles auto-magically
+# and have IO::File loaded for us
+{
+    is( $INC{'IO/File.pm'}, undef, "IO::File not loaded" );
+    my $var = "";
+    open my $fh, ">", \$var;
+    ok( eval { $fh->autoflush(1); 1 }, '$fh->autoflush(1) lives' );
+    ok( $INC{'IO/File.pm'}, "IO::File now loaded" );
+}