From: Nicholas Clark Date: Sun, 16 Apr 2006 15:04:36 +0000 (+0000) Subject: A scalar reference returned from a coderef in @INC is treated as the X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=34113e50dc4bebd1212d8cbfbf09a86a40b7a699;p=p5sagit%2Fp5-mst-13.2.git A scalar reference returned from a coderef in @INC is treated as the initial "content" of the file. When it is exhausted input is taken from a real file handle, or a generator sub, if either exists. p4raw-id: //depot/perl@27849 --- diff --git a/pp_ctl.c b/pp_ctl.c index 43db9de..d783e1f 100644 --- a/pp_ctl.c +++ b/pp_ctl.c @@ -3067,6 +3067,7 @@ PP(pp_require) const I32 gimme = GIMME_V; int filter_has_file = 0; PerlIO *tryrsfp = NULL; + SV *filter_cache = NULL; SV *filter_state = NULL; SV *filter_sub = NULL; SV *hook_sv = NULL; @@ -3174,6 +3175,16 @@ PP(pp_require) SP -= count - 1; arg = SP[i++]; + if (SvROK(arg) && (SvTYPE(SvRV(arg)) <= SVt_PVLV) + && !isGV_with_GP(SvRV(arg))) { + filter_cache = SvRV(arg); + SvREFCNT_inc_void_NN(filter_cache); + + if (i < count) { + arg = SP[i++]; + } + } + if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVGV) { arg = SvRV(arg); } @@ -3205,11 +3216,11 @@ PP(pp_require) filter_state = SP[i]; SvREFCNT_inc_simple_void(filter_state); } + } - if (!tryrsfp) { - tryrsfp = PerlIO_open(BIT_BUCKET, - PERL_SCRIPT_MODE); - } + if (!tryrsfp && (filter_cache || filter_sub)) { + tryrsfp = PerlIO_open(BIT_BUCKET, + PERL_SCRIPT_MODE); } SP--; } @@ -3224,6 +3235,10 @@ PP(pp_require) } filter_has_file = 0; + if (filter_cache) { + SvREFCNT_dec(filter_cache); + filter_cache = NULL; + } if (filter_state) { SvREFCNT_dec(filter_state); filter_state = NULL; @@ -3361,11 +3376,12 @@ PP(pp_require) SAVESPTR(PL_compiling.cop_io); PL_compiling.cop_io = NULL; - if (filter_sub) { + if (filter_sub || filter_cache) { SV * const datasv = filter_add(S_run_user_filter, NULL); IoLINES(datasv) = filter_has_file; IoTOP_GV(datasv) = (GV *)filter_state; IoBOTTOM_GV(datasv) = (GV *)filter_sub; + IoFMT_GV(datasv) = (GV *)filter_cache; } /* switch to eval mode */ @@ -4519,15 +4535,11 @@ S_run_user_filter(pTHX_ int idx, SV *buf_sv, int maxlen) SV * const filter_state = (SV *)IoTOP_GV(datasv); SV * const filter_sub = (SV *)IoBOTTOM_GV(datasv); int status = 0; - /* Filter API says that the filter appends to the contents of the buffer. - Usually the buffer is "", so the details don't matter. But if it's not, - then clearly what it contains is already filtered by this filter, so we - don't want to pass it in a second time. - I'm going to use a mortal in case the upstream filter croaks. */ SV *upstream; STRLEN got_len; const char *got_p; const char *prune_from = NULL; + bool read_from_cache = FALSE; /* I was having segfault trouble under Linux 2.2.5 after a parse error occured. (Had to hack around it with a test @@ -4567,9 +4579,15 @@ S_run_user_filter(pTHX_ int idx, SV *buf_sv, int maxlen) maxlen -= cache_len; } SvOK_off(cache); + read_from_cache = TRUE; } } + /* Filter API says that the filter appends to the contents of the buffer. + Usually the buffer is "", so the details don't matter. But if it's not, + then clearly what it contains is already filtered by this filter, so we + don't want to pass it in a second time. + I'm going to use a mortal in case the upstream filter croaks. */ upstream = ((SvOK(buf_sv) && sv_len(buf_sv)) || SvGMAGICAL(buf_sv)) ? sv_newmortal() : buf_sv; SvUPGRADE(upstream, SVt_PV); @@ -4578,8 +4596,7 @@ S_run_user_filter(pTHX_ int idx, SV *buf_sv, int maxlen) status = FILTER_READ(idx+1, upstream, 0); } - assert(filter_sub); - if (status >= 0) { + if (filter_sub && status >= 0) { dSP; int count; @@ -4650,7 +4667,11 @@ S_run_user_filter(pTHX_ int idx, SV *buf_sv, int maxlen) status = 1; } - if (upstream != buf_sv) { + /* If they are at EOF but buf_sv has something in it, then they may never + have touched the SV upstream, so it may be undefined. If we naively + concatenate it then we get a warning about use of uninitialised value. + */ + if (upstream != buf_sv && (SvOK(upstream) || SvGMAGICAL(upstream))) { sv_catsv(buf_sv, upstream); } @@ -4667,6 +4688,12 @@ S_run_user_filter(pTHX_ int idx, SV *buf_sv, int maxlen) } filter_del(S_run_user_filter); } + if (status == 0 && read_from_cache) { + /* If we read some data from the cache (and by getting here it implies + that we emptied the cache) then we aren't yet at EOF, and mustn't + report that to our caller. */ + return 1; + } return status; } diff --git a/t/op/incfilter.t b/t/op/incfilter.t index 97ce37a..0a5381e 100644 --- a/t/op/incfilter.t +++ b/t/op/incfilter.t @@ -14,7 +14,7 @@ BEGIN { use strict; use Filter::Util::Call; -plan(tests => 128); +plan(tests => 141); unshift @INC, sub { no warnings 'uninitialized'; @@ -44,14 +44,17 @@ do \&generator or die; # Check that the array dereferencing works ready for the more complex tests: do [\&generator] or die; -do [sub { - my $param = $_[1]; - is (ref $param, 'ARRAY', "Got our parameter"); - $_ = shift @$param; - return defined $_ ? 1 : 0; - }, ["pass('Can return generators which take state');\n", - "pass('And return multiple lines');\n", - ]] or die; +sub generator_with_state { + my $param = $_[1]; + is (ref $param, 'ARRAY', "Got our parameter"); + $_ = shift @$param; + return defined $_ ? 1 : 0; +} + +do [\&generator_with_state, + ["pass('Can return generators which take state');\n", + "pass('And return multiple lines');\n", + ]] or die; open $fh, "<", \'fail("File handles and filters work from \@INC");'; @@ -173,3 +176,27 @@ pass("You should see this line thrice"); EOC do [$fh, sub {$_ .= $_ . $_; return;}] or die; + +do \"pass\n(\n'Scalar references are treated as initial file contents'\n)\n" +or die; + +open $fh, "<", \"ss('The file is concatentated');"; + +do [\'pa', $fh] or die; + +open $fh, "<", \"ff('Gur svygre vf bayl eha ba gur svyr');"; + +do [\'pa', $fh, sub {tr/A-Za-z/N-ZA-Mn-za-m/; return;}] or die; + +open $fh, "<", \"SS('State also works');"; + +do [\'pa', $fh, sub {s/($_[1])/lc $1/ge; return;}, "S"] or die; + +@lines = ('ss', '(', "'you can use a generator'", ')'); + +do [\'pa', \&generator] or die; + +do [\'pa', \&generator_with_state, + ["ss('And generators which take state');\n", + "pass('And return multiple lines');\n", + ]] or die;