be adaptive about deleting the realloc filter
Zefram [Wed, 1 Feb 2012 21:40:24 +0000 (21:40 +0000)]
Filters can only be deleted in the correct order (reverse of the order in
which they were added).  Insisting on deleting the filter would break if
another filter were added after ours and is still around.  Not deleting
the filter at all would break if another filter were added earlier and
attempts to delete itself later.  We can play nicely to the maximum
possible extent by deleting our filter iff it is currently deletable
(i.e., it is on the top of the filter stack).  Can still run into trouble
in more complex situations, but can't avoid that.

Changes
Declare.xs
t/filter0.t [new file with mode: 0644]
t/filter1.t [new file with mode: 0644]

diff --git a/Changes b/Changes
index cab43f9..2af6d1f 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,7 @@
 Changes for Devel-Declare
 
+  - Be adaptive about whether to delete the line reallocation filter,
+    so as to play as nicely as possible with other kinds of source filter.
   - Document that injecting newlines doesn't work.
   - Fix a C declaration after statement, which broke compatibility with
     older compilers (Jan Dubois).
index 4952520..44d0a72 100644 (file)
@@ -399,9 +399,23 @@ STATIC OP *dd_ck_entereval(pTHX_ OP *o, void *user_data) {
 
 static I32 dd_filter_realloc(pTHX_ int idx, SV *sv, int maxlen)
 {
+  SV *filter_datasv;
   const I32 count = FILTER_READ(idx+1, sv, maxlen);
   SvGROW(sv, DD_PREFERRED_LINESTR_SIZE);
-  /* filter_del(dd_filter_realloc); */
+  /* Filters can only be deleted in the correct order (reverse of the
+     order in which they were added).  Insisting on deleting the filter
+     here would break if another filter were added after ours and is
+     still around.  Not deleting the filter at all would break if another
+     filter were added earlier and attempts to delete itself later.
+     We can play nicely to the maximum possible extent by deleting our
+     filter iff it is currently deletable (i.e., it is on the top of
+     the filter stack).  Can still run into trouble in more complex
+     situations, but can't avoid that. */
+  if (PL_rsfp_filters && AvFILLp(PL_rsfp_filters) >= 0 &&
+      (filter_datasv = FILTER_DATA(AvFILLp(PL_rsfp_filters))) &&
+      IoANY(filter_datasv) == FPTR2DPTR(void *, dd_filter_realloc)) {
+    filter_del(dd_filter_realloc);
+  }
   return count;
 }
 
diff --git a/t/filter0.t b/t/filter0.t
new file mode 100644 (file)
index 0000000..b84b402
--- /dev/null
@@ -0,0 +1,25 @@
+use warnings;
+use strict;
+
+BEGIN {
+       eval { require Filter::Util::Call };
+       if($@ ne "") {
+               require Test::More;
+               Test::More::plan(skip_all => "Filter::Util::Call unavailable");
+       }
+}
+
+use Devel::Declare ();
+use Filter::Util::Call qw(filter_add filter_del);
+use Test::More tests => 2;
+
+sub my_quote($) { $_[0] }
+
+my $i = 0;
+
+BEGIN { Devel::Declare->setup_for(__PACKAGE__, { my_quote => { const => sub { } } }); }
+BEGIN { filter_add(sub { filter_del(); $_ .= "ok \$i++ == 0;"; return 1; }); }
+
+ok $i++ == 1;
+
+1;
diff --git a/t/filter1.t b/t/filter1.t
new file mode 100644 (file)
index 0000000..0240290
--- /dev/null
@@ -0,0 +1,25 @@
+use warnings;
+use strict;
+
+BEGIN {
+       eval { require Filter::Util::Call };
+       if($@ ne "") {
+               require Test::More;
+               Test::More::plan(skip_all => "Filter::Util::Call unavailable");
+       }
+}
+
+use Devel::Declare ();
+use Filter::Util::Call qw(filter_add filter_del);
+use Test::More tests => 2;
+
+sub my_quote($) { $_[0] }
+
+my $i = 0;
+
+BEGIN { filter_add(sub { filter_del(); $_ .= "ok \$i++ == 0;"; return 1; }); }
+BEGIN { Devel::Declare->setup_for(__PACKAGE__, { my_quote => { const => sub { } } }); }
+
+ok $i++ == 1;
+
+1;